From 6a58e3d32b411b2cc59ffcaca3abb40f6420f9a7 Mon Sep 17 00:00:00 2001 From: Qingpeng Li Date: Thu, 13 Oct 2022 10:54:45 +0000 Subject: [PATCH 001/607] improve `es5ClassCompat` robustness --- src/vs/workbench/api/common/extHostTypes.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index f7b89d987a9..d922d3ff504 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -29,15 +29,19 @@ import type * as vscode from 'vscode'; * */ function es5ClassCompat(target: Function): any { const interceptFunctions = { - apply: function () { - const args = arguments.length === 1 ? [] : arguments[1]; - return Reflect.construct(target, args, arguments[0].constructor); - }, - call: function () { - if (arguments.length === 0) { + apply: function (...args: any[]): any { + if (args.length === 0) { return Reflect.construct(target, []); } else { - const [thisArg, ...restArgs] = arguments; + const argsList = args.length === 1 ? [] : args[1]; + return Reflect.construct(target, argsList, args[0].constructor); + } + }, + call: function (...args: any[]): any { + if (args.length === 0) { + return Reflect.construct(target, []); + } else { + const [thisArg, ...restArgs] = args; return Reflect.construct(target, restArgs, thisArg.constructor); } } From 6909a3a0d1c51de65b7bd37ef2f29d1cb987c630 Mon Sep 17 00:00:00 2001 From: Antonio Prudenzano Date: Sun, 7 May 2023 19:17:22 +0200 Subject: [PATCH 002/607] added focus_in and focus_out events only on HTMLElement elements --- src/vs/base/browser/dom.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 917fa90ca76..3a555f3d597 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -921,8 +921,11 @@ class FocusTracker extends Disposable implements IFocusTracker { this._register(addDisposableListener(element, EventType.FOCUS, onFocus, true)); this._register(addDisposableListener(element, EventType.BLUR, onBlur, true)); - this._register(addDisposableListener(element, EventType.FOCUS_IN, () => this._refreshStateHandler())); - this._register(addDisposableListener(element, EventType.FOCUS_OUT, () => this._refreshStateHandler())); + if (element instanceof HTMLElement) { + this._register(addDisposableListener(element, EventType.FOCUS_IN, () => this._refreshStateHandler())); + this._register(addDisposableListener(element, EventType.FOCUS_OUT, () => this._refreshStateHandler())); + } + } refreshState() { From f1be6ea0a69cefb51af6e504ee8494e92511c493 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Thu, 27 Jul 2023 16:58:14 -0700 Subject: [PATCH 003/607] Set title and icon on chat view directly Fix microsoft/vscode-copilot-release#342 --- .../contrib/chat/browser/chatContributionServiceImpl.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts index 0176f7c10b6..e018afb26b0 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts @@ -105,12 +105,15 @@ export class ChatContributionService implements IChatContributionService { } private registerChatProvider(extension: Readonly, providerDescriptor: IRawChatProviderContribution): IDisposable { + const icon = providerDescriptor.icon ? resources.joinPath(extension.extensionLocation, providerDescriptor.icon) : Codicon.commentDiscussion; + const title = localize('chat.viewContainer.label', "Chat"); + // Register View Container const viewContainerId = CHAT_SIDEBAR_PANEL_ID + '.' + providerDescriptor.id; const viewContainer: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: viewContainerId, - title: localize('chat.viewContainer.label', "Chat"), - icon: providerDescriptor.icon ? resources.joinPath(extension.extensionLocation, providerDescriptor.icon) : Codicon.commentDiscussion, + title, + icon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [viewContainerId, { mergeViewWithContainerWhenSingleView: true }]), storageId: viewContainerId, hideIfEmpty: true, @@ -121,6 +124,8 @@ export class ChatContributionService implements IChatContributionService { const viewId = this.getViewIdForProvider(providerDescriptor.id); const viewDescriptor: IViewDescriptor[] = [{ id: viewId, + containerIcon: icon, + containerTitle: title, name: providerDescriptor.label, canToggleVisibility: false, canMoveView: true, From 1d2e4d3d0a7d30d0f743fe89b2f1678821703ae4 Mon Sep 17 00:00:00 2001 From: weartist Date: Fri, 4 Aug 2023 23:26:49 +0800 Subject: [PATCH 004/607] support click on "hidden lines" text to unfold #186406 --- .../widget/diffEditorWidget2/unchangedRanges.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 7272f56b11f..60f03b220a5 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -182,13 +182,15 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { h('div.top@top', { title: localize('diff.hiddenLines.top', 'Click or drag to show more above') }), h('div.center@content', { style: { display: 'flex' } }, [ h('div@first', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } }, - [$('a', { title: localize('showAll', 'Show all'), role: 'button', onclick: () => { this._unchangedRegion.showAll(undefined); } }, ...renderLabelWithIcons('$(unfold)'))] + [$('a', { title: localize('showAll', 'Show all'), role: 'button', onclick: () => { this.showAll(); } }, ...renderLabelWithIcons('$(unfold)'))] ), h('div@others', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } }), ]), h('div.bottom@bottom', { title: localize('diff.bottom', 'Click or drag to show more below'), role: 'button' }), ]); + private showAll() { this._unchangedRegion.showAll(undefined); } + constructor( private readonly _editor: ICodeEditor, _viewZone: PlaceholderViewZone, @@ -293,7 +295,15 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { if (!this.hide && true) { const lineCount = _unchangedRegion.getHiddenModifiedRange(reader).length; const linesHiddenText = localize('hiddenLines', '{0} Hidden Lines', lineCount); - children.push($('span', { title: linesHiddenText }, linesHiddenText)); + const span = $('span', { title: localize('diff.hiddenLines.expandAll', 'Double click to show all unchanged region') }, linesHiddenText); + addDisposableListener(span, 'dblclick', e => { + if (e.button !== 0) { + return; + } + e.preventDefault(); + this.showAll(); + }); + children.push(span); } // TODO@hediet implement breadcrumbs for collapsed regions From e5fe93178349d24b7c9a3a7a9645b6c80d1bae11 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 8 Aug 2023 11:30:22 +0200 Subject: [PATCH 005/607] voice - have a `VSFloat32Array` primitive that is serializable --- src/vs/base/common/buffer.ts | 32 ++++++++++++++++++++++++++ src/vs/base/common/marshalling.ts | 3 ++- src/vs/base/common/marshallingIds.ts | 3 ++- src/vs/base/test/common/buffer.test.ts | 21 ++++++++++++++++- 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index 08736ab8c0b..1981217fd1c 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Lazy } from 'vs/base/common/lazy'; +import { MarshalledId } from 'vs/base/common/marshallingIds'; import * as streams from 'vs/base/common/stream'; declare const Buffer: any; @@ -439,3 +440,34 @@ export function encodeBase64({ buffer }: VSBuffer, padded = true, urlSafe = fals return output; } + +export interface VSFloat32ArrayComponents { + readonly $mid: MarshalledId.Float32Array; + readonly values: number[]; +} + +export class VSFloat32Array { + + readonly buffer: Float32Array; + readonly byteLength: number; + + static wrap(actual: Float32Array): VSFloat32Array { + return new VSFloat32Array(actual); + } + + private constructor(buffer: Float32Array) { + this.buffer = buffer; + this.byteLength = this.buffer.byteLength; + } + + toJSON(): VSFloat32ArrayComponents { + return { + $mid: MarshalledId.Float32Array, + values: Array.from(this.buffer.map(value => value)) + }; + } + + static revive(raw: VSFloat32ArrayComponents): VSFloat32Array { + return VSFloat32Array.wrap(new Float32Array(raw.values)); + } +} diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 67a29703cdc..087ad5d0f58 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSBuffer } from 'vs/base/common/buffer'; +import { VSBuffer, VSFloat32Array } from 'vs/base/common/buffer'; import { regExpFlags } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { MarshalledId } from './marshallingIds'; @@ -54,6 +54,7 @@ export function revive(obj: any, depth = 0): Revived { case MarshalledId.Uri: return URI.revive(obj); case MarshalledId.Regexp: return new RegExp(obj.source, obj.flags); case MarshalledId.Date: return new Date(obj.source); + case MarshalledId.Float32Array: return VSFloat32Array.revive(obj); } if ( diff --git a/src/vs/base/common/marshallingIds.ts b/src/vs/base/common/marshallingIds.ts index abd7698ed92..7cb27d18f36 100644 --- a/src/vs/base/common/marshallingIds.ts +++ b/src/vs/base/common/marshallingIds.ts @@ -20,5 +20,6 @@ export const enum MarshalledId { NotebookCellActionContext, NotebookActionContext, TestItemContext, - Date + Date, + Float32Array } diff --git a/src/vs/base/test/common/buffer.test.ts b/src/vs/base/test/common/buffer.test.ts index 5a37943b658..969a96d9303 100644 --- a/src/vs/base/test/common/buffer.test.ts +++ b/src/vs/base/test/common/buffer.test.ts @@ -5,7 +5,8 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; -import { bufferedStreamToBuffer, bufferToReadable, bufferToStream, decodeBase64, encodeBase64, newWriteableBufferStream, readableToBuffer, streamToBuffer, VSBuffer } from 'vs/base/common/buffer'; +import { bufferedStreamToBuffer, bufferToReadable, bufferToStream, decodeBase64, encodeBase64, newWriteableBufferStream, readableToBuffer, streamToBuffer, VSBuffer, VSFloat32Array } from 'vs/base/common/buffer'; +import { parse, stringify } from 'vs/base/common/marshalling'; import { peekStream } from 'vs/base/common/stream'; suite('Buffer', () => { @@ -477,4 +478,22 @@ suite('Buffer', () => { assert.throws(() => decodeBase64('invalid!')); }); }); + + suite('Float32Array', () => { + + test('serialization', () => { + const array = new Float32Array(10); + for (let i = 0; i < array.length; i++) { + array[i] = i === 0 ? 0 : Math.random(); + } + + const buffer = VSFloat32Array.wrap(array); + const serialized = stringify(buffer); + const deserialized = parse(serialized); + + assert.ok(deserialized instanceof VSFloat32Array); + assert.deepStrictEqual(array, deserialized.buffer); + assert.deepStrictEqual(array.byteLength, deserialized.byteLength); + }); + }); }); From 707bffbdae207740940e86a11b8178978515f9d0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 8 Aug 2023 17:08:44 +0200 Subject: [PATCH 006/607] voice - scaffold a basic voice recorder service --- build/lib/i18n.resources.json | 4 + src/vs/code/electron-main/app.ts | 16 +- .../node/sharedProcess/sharedProcessMain.ts | 9 ++ .../common/voiceRecognitionService.ts | 31 ++++ .../node/voiceRecognitionService.ts | 38 +++++ .../electron-sandbox/chat.contribution.ts | 50 +++++++ .../voiceRecognitionService.ts | 9 ++ .../workbenchVoiceRecognitionService.ts | 140 ++++++++++++++++++ src/vs/workbench/workbench.desktop.main.ts | 5 + 9 files changed, 300 insertions(+), 2 deletions(-) create mode 100644 src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts create mode 100644 src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts create mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts create mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts create mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index f83225cc974..15a06c1a021 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -78,6 +78,10 @@ "name": "vs/workbench/services/assignment", "project": "vscode-workbench" }, + { + "name": "vs/workbench/services/voiceRecognition", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/extensions", "project": "vscode-workbench" diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index bd6fb95a75d..9c79498b8f5 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -162,24 +162,36 @@ export class CodeApplication extends Disposable { const isUrlFromWebview = (requestingUrl: string | undefined) => requestingUrl?.startsWith(`${Schemas.vscodeWebview}://`); + const allowedPermissionsInMainFrame = new Set([ + 'media' + ]); + const allowedPermissionsInWebview = new Set([ 'clipboard-read', 'clipboard-sanitized-write', ]); - session.defaultSession.setPermissionRequestHandler((_webContents, permission /* 'media' | 'geolocation' | 'notifications' | 'midiSysex' | 'pointerLock' | 'fullscreen' | 'openExternal' */, callback, details) => { + session.defaultSession.setPermissionRequestHandler((_webContents, permission, callback, details) => { if (isUrlFromWebview(details.requestingUrl)) { return callback(allowedPermissionsInWebview.has(permission)); } + if (details.isMainFrame && details.securityOrigin === 'vscode-file://vscode-app/') { + return callback(allowedPermissionsInMainFrame.has(permission)); + } + return callback(false); }); - session.defaultSession.setPermissionCheckHandler((_webContents, permission /* 'media' */, _origin, details) => { + session.defaultSession.setPermissionCheckHandler((_webContents, permission, _origin, details) => { if (isUrlFromWebview(details.requestingUrl)) { return allowedPermissionsInWebview.has(permission); } + if (details.isMainFrame && details.securityOrigin === 'vscode-file://vscode-app/') { + return allowedPermissionsInMainFrame.has(permission); + } + return false; }); diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index f911b0da431..7092cc2b8ec 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -112,6 +112,8 @@ import { IRemoteSocketFactoryService, RemoteSocketFactoryService } from 'vs/plat import { RemoteConnectionType } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; +import { VoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; class SharedProcessMain extends Disposable { @@ -351,6 +353,9 @@ class SharedProcessMain extends Disposable { // Remote Tunnel services.set(IRemoteTunnelService, new SyncDescriptor(RemoteTunnelService)); + // Voice Recognition + services.set(IVoiceRecognitionService, new SyncDescriptor(VoiceRecognitionService, undefined, false /* proxied to other processes */)); + return new InstantiationService(services); } @@ -408,6 +413,10 @@ class SharedProcessMain extends Disposable { // Remote Tunnel const remoteTunnelChannel = ProxyChannel.fromService(accessor.get(IRemoteTunnelService)); this.server.registerChannel('remoteTunnel', remoteTunnelChannel); + + // Voice Recognition + const voiceRecognitionChannel = ProxyChannel.fromService(accessor.get(IVoiceRecognitionService)); + this.server.registerChannel('voiceRecognition', voiceRecognitionChannel); } private registerErrorHandler(logService: ILogService): void { diff --git a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts new file mode 100644 index 00000000000..0cc3310df97 --- /dev/null +++ b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.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 { VSFloat32Array } from 'vs/base/common/buffer'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); + +export interface IAudioBuffer { + readonly sampleRate: 16000; + readonly channelCount: 1; + readonly length: number; + readonly channelData: VSFloat32Array; +} + +export interface IVoiceRecognitionService { + + readonly _serviceBrand: undefined; + + /** + * Given a buffer of audio data, attempts to + * transcribe the spoken words into text. + * + * @param buffer the audio data obtained from + * the microphone as PCM 32-bit float mono in + * 16khz. + */ + transcribe(buffer: IAudioBuffer): Promise; +} diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts new file mode 100644 index 00000000000..cb57192e299 --- /dev/null +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.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 { ILogService } from 'vs/platform/log/common/log'; +import { IAudioBuffer, IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; + +export class VoiceRecognitionService implements IVoiceRecognitionService { + + declare readonly _serviceBrand: undefined; + + constructor( + @ILogService private readonly logService: ILogService + ) { } + + async transcribe(buffer: IAudioBuffer): Promise { + this.logService.info(`[voice] transcribe(${buffer.length}): Begin`); + + const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; + if (!modulePath) { + throw new Error('Voice recognition not yet supported!'); + } + + const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; length: number; sampleRate: 16000; channelData: Float32Array }) => Promise } = require.__$__nodeRequire(modulePath); + + const text = await voiceModule.transcribe({ + channelCount: buffer.channelCount, + length: buffer.length, + sampleRate: buffer.sampleRate, + channelData: buffer.channelData.buffer + }); + + this.logService.info(`[voice] transcribe(${buffer.length}): End (text: "${text}"))`); + + return text; + } +} diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts new file mode 100644 index 00000000000..0f0c9474e27 --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { toAction } from 'vs/base/common/actions'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { INotificationService, NotificationPriority, Severity } from 'vs/platform/notification/common/notification'; +import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; + +let activeVoiceTranscription: DisposableStore | undefined; + +function stopVoiceTranscription() { + activeVoiceTranscription?.dispose(); + activeVoiceTranscription = undefined; +} + +CommandsRegistry.registerCommand('workbench.action.toggleVoiceTranscription', async services => { + if (activeVoiceTranscription) { + stopVoiceTranscription(); + } else { + const voiceRecognitionService = services.get(IWorkbenchVoiceRecognitionService); + const notificationService = services.get(INotificationService); + + activeVoiceTranscription = new DisposableStore(); + + const cts = new CancellationTokenSource(); + activeVoiceTranscription.add(toDisposable(() => cts.dispose(true))); + + const voiceTranscriptionNotification = notificationService.notify({ + severity: Severity.Info, + priority: NotificationPriority.URGENT, + sticky: true, + message: 'Listening...', + actions: { + primary: [ + toAction({ id: 'stopVoiceTranscription', label: 'Stop', run: () => stopVoiceTranscription() }) + ] + } + }); + + activeVoiceTranscription.add(toDisposable(() => voiceTranscriptionNotification.close())); + + activeVoiceTranscription.add(voiceRecognitionService.transcribe(cts.token)(text => { + voiceTranscriptionNotification.updateMessage(text); + })); + } +}); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts new file mode 100644 index 00000000000..e8b4e771ffa --- /dev/null +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; +import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; + +registerSharedProcessRemoteService(IVoiceRecognitionService, 'voiceRecognition'); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts new file mode 100644 index 00000000000..a98131381ce --- /dev/null +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { VSFloat32Array } from 'vs/base/common/buffer'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { DeferredPromise } from 'vs/base/common/async'; + +export const IWorkbenchVoiceRecognitionService = createDecorator('workbenchVoiceRecognitionService'); + +export interface IWorkbenchVoiceRecognitionService { + + readonly _serviceBrand: undefined; + + /** + * Starts listening to the microphone transcribing the voice to text. + * + * @param cancellation a cancellation token to stop transcribing and + * listening to the microphone. + */ + transcribe(cancellation: CancellationToken): Event; +} + +// TODO@voice +// - load `navigator.mediaDevices.getUserMedia` lazily on startup? or would it trigger a permission prompt? +// - figure out the ugly `any` cast for AudioContext +// - how to prevent data processing accumulation when processing is slow? +// - how to make this a singleton service that enables ref-counting on multiple callers? +// - cancellation should flow to the shared process +// - voice module should directly transcribe the PCM32 data +// - we should transfer the Float32Array directly without serialisation overhead + +export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { + + declare readonly _serviceBrand: undefined; + + private static readonly AUDIO_TIME_SLICE = 2000; + private static readonly AUDIO_MIME_TYPE = 'audio/webm;codecs=opus'; + + constructor( + @IVoiceRecognitionService private readonly voiceRecognitionService: IVoiceRecognitionService, + @IProgressService private readonly progressService: IProgressService + ) { } + + transcribe(cancellation: CancellationToken): Event { + const cts = new CancellationTokenSource(cancellation); + const emitter = new Emitter(); + cancellation.onCancellationRequested(() => emitter.dispose()); + + this.doTranscribe(emitter, cts.token); + + return emitter.event; + } + + private async doTranscribe(emitter: Emitter, token: CancellationToken): Promise { + return this.progressService.withProgress({ + location: ProgressLocation.Window, + title: localize('voiceTranscription', "Voice Transcription"), + }, async progress => { + const recordingDone = new DeferredPromise(); + + progress.report({ message: localize('voiceTranscriptionGettingReady', "Getting microphone ready...") }); + + const audioDevice = await navigator.mediaDevices.getUserMedia({ audio: true }); + + if (token.isCancellationRequested) { + return; + } + + const audioRecorder = new MediaRecorder(audioDevice, { mimeType: WorkbenchVoiceRecognitionService.AUDIO_MIME_TYPE }); + audioRecorder.start(WorkbenchVoiceRecognitionService.AUDIO_TIME_SLICE); + + token.onCancellationRequested(() => { + audioRecorder.stop(); + recordingDone.complete(); + }); + + progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); + + const chunks: Blob[] = []; + audioRecorder.ondataavailable = e => { + chunks.push(e.data); + + this.doTranscribeChunk(chunks, emitter, token); + }; + + return recordingDone.p; + }); + } + + private async doTranscribeChunk(chunks: Blob[], emitter: Emitter, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return; + } + + const blob = new Blob(chunks); + const blobBuffer = await blob.arrayBuffer(); + if (token.isCancellationRequested) { + return; + } + + const audioContextOptions = { + sampleRate: 16000 as const, + channelCount: 1 as const, + echoCancellation: false, + autoGainControl: true, + noiseSuppression: true + }; + + const context = new AudioContext(audioContextOptions as any); + + const audioBuffer = await context.decodeAudioData(blobBuffer); + if (token.isCancellationRequested) { + return; + } + + const text = await this.voiceRecognitionService.transcribe({ + sampleRate: audioContextOptions.sampleRate, + channelCount: audioContextOptions.channelCount, + length: audioBuffer.length, + channelData: VSFloat32Array.wrap(audioBuffer.getChannelData(0)) + }); + + if (token.isCancellationRequested) { + return; + } + + emitter.fire(text); + } +} + +// Register Service +registerSingleton(IWorkbenchVoiceRecognitionService, WorkbenchVoiceRecognitionService, InstantiationType.Delayed); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index e943de4afff..b3367ac6ea2 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -77,6 +77,8 @@ import 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentServi import 'vs/workbench/services/integrity/electron-sandbox/integrityService'; import 'vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService'; import 'vs/workbench/services/checksum/electron-sandbox/checksumService'; +import 'vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService'; +import 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; import 'vs/platform/remote/electron-sandbox/sharedProcessTunnelService'; import 'vs/workbench/services/tunnel/electron-sandbox/tunnelService'; import 'vs/platform/diagnostics/electron-sandbox/diagnosticsService'; @@ -169,6 +171,9 @@ import 'vs/workbench/contrib/mergeEditor/electron-sandbox/mergeEditor.contributi // Remote Tunnel import 'vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution'; +// Chat +import 'vs/workbench/contrib/chat/electron-sandbox/chat.contribution'; + //#endregion From 2eee37034d9c675a029495298c4fd3212f6c90bd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Aug 2023 07:53:03 +0200 Subject: [PATCH 007/607] voice - reuse `AudioContext` --- .../workbenchVoiceRecognitionService.ts | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index a98131381ce..43ff05ece57 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -42,6 +42,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit declare readonly _serviceBrand: undefined; private static readonly AUDIO_TIME_SLICE = 2000; + private static readonly AUDIO_SAMPLE_RATE = 16000; + private static readonly AUDIO_CHANNELS = 1; private static readonly AUDIO_MIME_TYPE = 'audio/webm;codecs=opus'; constructor( @@ -74,7 +76,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit return; } - const audioRecorder = new MediaRecorder(audioDevice, { mimeType: WorkbenchVoiceRecognitionService.AUDIO_MIME_TYPE }); + const audioRecorder = new MediaRecorder(audioDevice, { mimeType: WorkbenchVoiceRecognitionService.AUDIO_MIME_TYPE, audioBitsPerSecond: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE }); audioRecorder.start(WorkbenchVoiceRecognitionService.AUDIO_TIME_SLICE); token.onCancellationRequested(() => { @@ -84,18 +86,28 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); + const audioContextOptions = { + sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, + channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, + echoCancellation: false, + autoGainControl: true, + noiseSuppression: true + }; + + const context = new AudioContext(audioContextOptions as any); + const chunks: Blob[] = []; audioRecorder.ondataavailable = e => { chunks.push(e.data); - this.doTranscribeChunk(chunks, emitter, token); + this.doTranscribeChunk(context, chunks, emitter, token); }; return recordingDone.p; }); } - private async doTranscribeChunk(chunks: Blob[], emitter: Emitter, token: CancellationToken): Promise { + private async doTranscribeChunk(context: AudioContext, chunks: Blob[], emitter: Emitter, token: CancellationToken): Promise { if (token.isCancellationRequested) { return; } @@ -106,24 +118,14 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit return; } - const audioContextOptions = { - sampleRate: 16000 as const, - channelCount: 1 as const, - echoCancellation: false, - autoGainControl: true, - noiseSuppression: true - }; - - const context = new AudioContext(audioContextOptions as any); - const audioBuffer = await context.decodeAudioData(blobBuffer); if (token.isCancellationRequested) { return; } const text = await this.voiceRecognitionService.transcribe({ - sampleRate: audioContextOptions.sampleRate, - channelCount: audioContextOptions.channelCount, + sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, + channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, length: audioBuffer.length, channelData: VSFloat32Array.wrap(audioBuffer.getChannelData(0)) }); From 47f0356460be5dd265065b5e1064aaebc77843b5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Aug 2023 10:54:33 +0200 Subject: [PATCH 008/607] voice - strip out non-speech tokens --- .../voiceRecognition/node/voiceRecognitionService.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index cb57192e299..687ce112473 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -22,13 +22,16 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { throw new Error('Voice recognition not yet supported!'); } - const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; length: number; sampleRate: 16000; channelData: Float32Array }) => Promise } = require.__$__nodeRequire(modulePath); + const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; length: number; sampleRate: 16000; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); const text = await voiceModule.transcribe({ channelCount: buffer.channelCount, length: buffer.length, sampleRate: buffer.sampleRate, channelData: buffer.channelData.buffer + }, { + language: 'en', + suppressNonSpeechTokens: true }); this.logService.info(`[voice] transcribe(${buffer.length}): End (text: "${text}"))`); From 8b199750a4941b2cd13773c079097a33f31ba3b6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Aug 2023 11:38:56 +0200 Subject: [PATCH 009/607] voice - some options tweaks --- .../common/voiceRecognitionService.ts | 1 + .../node/voiceRecognitionService.ts | 8 +++--- .../workbenchVoiceRecognitionService.ts | 26 +++++++++++-------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts index 0cc3310df97..42650f5924d 100644 --- a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts @@ -10,6 +10,7 @@ export const IVoiceRecognitionService = createDecorator { + const now = Date.now(); this.logService.info(`[voice] transcribe(${buffer.length}): Begin`); const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; @@ -22,19 +23,20 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { throw new Error('Voice recognition not yet supported!'); } - const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; length: number; sampleRate: 16000; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); + const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; length: number; sampleRate: 16000; sampleSize: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); const text = await voiceModule.transcribe({ + sampleRate: buffer.sampleRate, + sampleSize: buffer.sampleSize, channelCount: buffer.channelCount, length: buffer.length, - sampleRate: buffer.sampleRate, channelData: buffer.channelData.buffer }, { language: 'en', suppressNonSpeechTokens: true }); - this.logService.info(`[voice] transcribe(${buffer.length}): End (text: "${text}"))`); + this.logService.info(`[voice] transcribe(${buffer.length}): End (text: "${text}", took: ${Date.now() - now}ms))`); return text; } diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 43ff05ece57..ca0846c1c01 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -30,7 +30,6 @@ export interface IWorkbenchVoiceRecognitionService { // TODO@voice // - load `navigator.mediaDevices.getUserMedia` lazily on startup? or would it trigger a permission prompt? -// - figure out the ugly `any` cast for AudioContext // - how to prevent data processing accumulation when processing is slow? // - how to make this a singleton service that enables ref-counting on multiple callers? // - cancellation should flow to the shared process @@ -41,8 +40,9 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit declare readonly _serviceBrand: undefined; - private static readonly AUDIO_TIME_SLICE = 2000; + private static readonly AUDIO_TIME_SLICE = 4000; private static readonly AUDIO_SAMPLE_RATE = 16000; + private static readonly AUDIO_SAMPLE_SIZE = 16; private static readonly AUDIO_CHANNELS = 1; private static readonly AUDIO_MIME_TYPE = 'audio/webm;codecs=opus'; @@ -70,7 +70,15 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit progress.report({ message: localize('voiceTranscriptionGettingReady', "Getting microphone ready...") }); - const audioDevice = await navigator.mediaDevices.getUserMedia({ audio: true }); + const audioDevice = await navigator.mediaDevices.getUserMedia({ + audio: { + sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, + sampleSize: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_SIZE, + channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, + autoGainControl: true, + noiseSuppression: true + } + }); if (token.isCancellationRequested) { return; @@ -86,15 +94,10 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); - const audioContextOptions = { + const context = new AudioContext({ sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, - channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - echoCancellation: false, - autoGainControl: true, - noiseSuppression: true - }; - - const context = new AudioContext(audioContextOptions as any); + latencyHint: 'interactive' + }); const chunks: Blob[] = []; audioRecorder.ondataavailable = e => { @@ -125,6 +128,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit const text = await this.voiceRecognitionService.transcribe({ sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, + sampleSize: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_SIZE, channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, length: audioBuffer.length, channelData: VSFloat32Array.wrap(audioBuffer.getChannelData(0)) From ab9a1a4d77b7bab7be6a50599be56eb9bb1a082f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Aug 2023 15:25:42 +0200 Subject: [PATCH 010/607] voice - leverage audio worklet --- .../common/voiceRecognitionService.ts | 1 - .../node/voiceRecognitionService.ts | 7 +- .../electron-sandbox/chat.contribution.ts | 4 +- .../bufferInputAudioProcessor.js | 67 +++++++++++++++++++ .../workbenchVoiceRecognitionService.ts | 66 +++++++++--------- 5 files changed, 107 insertions(+), 38 deletions(-) create mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js diff --git a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts index 42650f5924d..3accb99ed0a 100644 --- a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts @@ -12,7 +12,6 @@ export interface IAudioBuffer { readonly sampleRate: 16000; readonly sampleSize: 16; readonly channelCount: 1; - readonly length: number; readonly channelData: VSFloat32Array; } diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index 1e48102b675..a9c750ec97d 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -16,27 +16,26 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { async transcribe(buffer: IAudioBuffer): Promise { const now = Date.now(); - this.logService.info(`[voice] transcribe(${buffer.length}): Begin`); + this.logService.info(`[voice] transcribe(${buffer.channelData.buffer.length}): Begin`); const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; if (!modulePath) { throw new Error('Voice recognition not yet supported!'); } - const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; length: number; sampleRate: 16000; sampleSize: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); + const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; sampleRate: 16000; sampleSize: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); const text = await voiceModule.transcribe({ sampleRate: buffer.sampleRate, sampleSize: buffer.sampleSize, channelCount: buffer.channelCount, - length: buffer.length, channelData: buffer.channelData.buffer }, { language: 'en', suppressNonSpeechTokens: true }); - this.logService.info(`[voice] transcribe(${buffer.length}): End (text: "${text}", took: ${Date.now() - now}ms))`); + this.logService.info(`[voice] transcribe(${buffer.channelData.buffer.length}): End (text: "${text}", took: ${Date.now() - now}ms))`); return text; } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts index 0f0c9474e27..45bb21c29ef 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -44,7 +44,9 @@ CommandsRegistry.registerCommand('workbench.action.toggleVoiceTranscription', as activeVoiceTranscription.add(toDisposable(() => voiceTranscriptionNotification.close())); activeVoiceTranscription.add(voiceRecognitionService.transcribe(cts.token)(text => { - voiceTranscriptionNotification.updateMessage(text); + if (text) { + voiceTranscriptionNotification.updateMessage(text); + } })); } }); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js new file mode 100644 index 00000000000..bed2b6ea63c --- /dev/null +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +// @ts-ignore +class BufferInputAudioProcessor extends AudioWorkletProcessor { + + constructor() { + super(); + + this.channelCount = 1; + this.bufferTimespan = 4000; + this.startTime = undefined; + + this.allInputChannelDataBuffer = undefined; + this.currentInputChannelDataBuffer = []; // buffer over the duration of bufferTimespan + } + + /** + * @param {[[Float32Array]]} inputs + */ + process(inputs) { + if (this.startTime === undefined) { + this.startTime = Date.now(); + } + + const inputChannelData = inputs[0][0]; + this.currentInputChannelDataBuffer.push(inputChannelData.slice(0)); + + if (Date.now() - this.startTime > this.bufferTimespan) { + const currentInputChannelDataBuffer = this.currentInputChannelDataBuffer; + this.currentInputChannelDataBuffer = []; + + this.allInputChannelDataBuffer = this._joinFloat32Arrays(this.allInputChannelDataBuffer ? [this.allInputChannelDataBuffer, ...currentInputChannelDataBuffer] : currentInputChannelDataBuffer); + + // @ts-ignore + this.port.postMessage(this.allInputChannelDataBuffer); + + this.startTime = Date.now(); + } + + return true; + } + + /** + * @param {Float32Array[]} float32Arrays + * @returns {Float32Array} + */ + _joinFloat32Arrays(float32Arrays) { + const result = new Float32Array(float32Arrays.reduce((acc, curr) => acc + curr.length, 0)); + + let offset = 0; + for (const float32Array of float32Arrays) { + result.set(float32Array, offset); + offset += float32Array.length; + } + + return result; + } +} + +// @ts-ignore +registerProcessor('buffer-input-audio-processor', BufferInputAudioProcessor); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index ca0846c1c01..1e3c986de15 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -12,6 +12,7 @@ import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/vo import { Emitter, Event } from 'vs/base/common/event'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { DeferredPromise } from 'vs/base/common/async'; +import { FileAccess } from 'vs/base/common/network'; export const IWorkbenchVoiceRecognitionService = createDecorator('workbenchVoiceRecognitionService'); @@ -28,6 +29,12 @@ export interface IWorkbenchVoiceRecognitionService { transcribe(cancellation: CancellationToken): Event; } +class BufferInputAudioNode extends AudioWorkletNode { + constructor(context: BaseAudioContext, options: AudioWorkletNodeOptions) { + super(context, 'buffer-input-audio-processor', options); + } +} + // TODO@voice // - load `navigator.mediaDevices.getUserMedia` lazily on startup? or would it trigger a permission prompt? // - how to prevent data processing accumulation when processing is slow? @@ -40,11 +47,9 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit declare readonly _serviceBrand: undefined; - private static readonly AUDIO_TIME_SLICE = 4000; private static readonly AUDIO_SAMPLE_RATE = 16000; private static readonly AUDIO_SAMPLE_SIZE = 16; private static readonly AUDIO_CHANNELS = 1; - private static readonly AUDIO_MIME_TYPE = 'audio/webm;codecs=opus'; constructor( @IVoiceRecognitionService private readonly voiceRecognitionService: IVoiceRecognitionService, @@ -70,7 +75,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit progress.report({ message: localize('voiceTranscriptionGettingReady', "Getting microphone ready...") }); - const audioDevice = await navigator.mediaDevices.getUserMedia({ + const microphoneDevice = await navigator.mediaDevices.getUserMedia({ audio: { sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, sampleSize: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_SIZE, @@ -84,44 +89,42 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit return; } - const audioRecorder = new MediaRecorder(audioDevice, { mimeType: WorkbenchVoiceRecognitionService.AUDIO_MIME_TYPE, audioBitsPerSecond: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE }); - audioRecorder.start(WorkbenchVoiceRecognitionService.AUDIO_TIME_SLICE); - - token.onCancellationRequested(() => { - audioRecorder.stop(); - recordingDone.complete(); - }); - - progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); - - const context = new AudioContext({ + const audioContext = new AudioContext({ sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, latencyHint: 'interactive' }); - const chunks: Blob[] = []; - audioRecorder.ondataavailable = e => { - chunks.push(e.data); + const microphoneSource = audioContext.createMediaStreamSource(microphoneDevice); - this.doTranscribeChunk(context, chunks, emitter, token); + token.onCancellationRequested(() => { + microphoneDevice.getTracks().forEach(track => track.stop()); + microphoneSource.disconnect(); + audioContext.close(); + recordingDone.complete(); + }); + + await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js').toString(true)); + + const bufferInputAudioTarget = new BufferInputAudioNode(audioContext, { + channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, + channelCountMode: 'explicit' + }); + + microphoneSource.connect(bufferInputAudioTarget); + + progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); + + bufferInputAudioTarget.port.onmessage = async e => { + if (e.data instanceof Float32Array) { + this.doTranscribeChunk(e.data, emitter, token); + } }; return recordingDone.p; }); } - private async doTranscribeChunk(context: AudioContext, chunks: Blob[], emitter: Emitter, token: CancellationToken): Promise { - if (token.isCancellationRequested) { - return; - } - - const blob = new Blob(chunks); - const blobBuffer = await blob.arrayBuffer(); - if (token.isCancellationRequested) { - return; - } - - const audioBuffer = await context.decodeAudioData(blobBuffer); + private async doTranscribeChunk(data: Float32Array, emitter: Emitter, token: CancellationToken): Promise { if (token.isCancellationRequested) { return; } @@ -130,8 +133,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, sampleSize: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_SIZE, channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - length: audioBuffer.length, - channelData: VSFloat32Array.wrap(audioBuffer.getChannelData(0)) + channelData: VSFloat32Array.wrap(data) }); if (token.isCancellationRequested) { From 652e2d069c57887ec97f3ebac29533e44919cf14 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 9 Aug 2023 20:28:46 +0200 Subject: [PATCH 011/607] voice - update build script includes --- build/gulpfile.reh.js | 4 ---- build/gulpfile.vscode.js | 3 +-- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index a235f55c79e..592157f8d76 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -62,10 +62,6 @@ const serverResources = [ // Performance 'out-build/vs/base/common/performance.js', - // Watcher - 'out-build/vs/platform/files/**/*.exe', - 'out-build/vs/platform/files/**/*.md', - // Process monitor 'out-build/vs/base/node/cpuUsage.sh', 'out-build/vs/base/node/ps.sh', diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 9505e8fe5ca..2093df79b16 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -72,10 +72,9 @@ const vscodeResources = [ 'out-build/vs/workbench/contrib/terminal/browser/media/*.sh', 'out-build/vs/workbench/contrib/terminal/browser/media/*.zsh', 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', + 'out-build/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', - 'out-build/vs/platform/files/**/*.exe', - 'out-build/vs/platform/files/**/*.md', '!**/test/**' ]; From 03ffebe2fcd06034091f47d996c1ebae7329e327 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Aug 2023 12:43:37 +0200 Subject: [PATCH 012/607] voice - drop `VSFloat32Array` and encode `float32` into a `VSBuffer` --- src/vs/base/common/buffer.ts | 32 ------------ src/vs/base/common/marshalling.ts | 3 +- src/vs/base/test/common/buffer.test.ts | 21 +------- .../node/sharedProcess/sharedProcessMain.ts | 1 - .../common/voiceRecognitionService.ts | 17 +++---- .../node/voiceRecognitionService.ts | 40 +++++++++++---- .../bufferInputAudioProcessor.js | 50 +++++++++++++------ .../workbenchVoiceRecognitionService.ts | 18 +++---- 8 files changed, 81 insertions(+), 101 deletions(-) diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index 1981217fd1c..08736ab8c0b 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Lazy } from 'vs/base/common/lazy'; -import { MarshalledId } from 'vs/base/common/marshallingIds'; import * as streams from 'vs/base/common/stream'; declare const Buffer: any; @@ -440,34 +439,3 @@ export function encodeBase64({ buffer }: VSBuffer, padded = true, urlSafe = fals return output; } - -export interface VSFloat32ArrayComponents { - readonly $mid: MarshalledId.Float32Array; - readonly values: number[]; -} - -export class VSFloat32Array { - - readonly buffer: Float32Array; - readonly byteLength: number; - - static wrap(actual: Float32Array): VSFloat32Array { - return new VSFloat32Array(actual); - } - - private constructor(buffer: Float32Array) { - this.buffer = buffer; - this.byteLength = this.buffer.byteLength; - } - - toJSON(): VSFloat32ArrayComponents { - return { - $mid: MarshalledId.Float32Array, - values: Array.from(this.buffer.map(value => value)) - }; - } - - static revive(raw: VSFloat32ArrayComponents): VSFloat32Array { - return VSFloat32Array.wrap(new Float32Array(raw.values)); - } -} diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 087ad5d0f58..67a29703cdc 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSBuffer, VSFloat32Array } from 'vs/base/common/buffer'; +import { VSBuffer } from 'vs/base/common/buffer'; import { regExpFlags } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import { MarshalledId } from './marshallingIds'; @@ -54,7 +54,6 @@ export function revive(obj: any, depth = 0): Revived { case MarshalledId.Uri: return URI.revive(obj); case MarshalledId.Regexp: return new RegExp(obj.source, obj.flags); case MarshalledId.Date: return new Date(obj.source); - case MarshalledId.Float32Array: return VSFloat32Array.revive(obj); } if ( diff --git a/src/vs/base/test/common/buffer.test.ts b/src/vs/base/test/common/buffer.test.ts index 969a96d9303..5a37943b658 100644 --- a/src/vs/base/test/common/buffer.test.ts +++ b/src/vs/base/test/common/buffer.test.ts @@ -5,8 +5,7 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; -import { bufferedStreamToBuffer, bufferToReadable, bufferToStream, decodeBase64, encodeBase64, newWriteableBufferStream, readableToBuffer, streamToBuffer, VSBuffer, VSFloat32Array } from 'vs/base/common/buffer'; -import { parse, stringify } from 'vs/base/common/marshalling'; +import { bufferedStreamToBuffer, bufferToReadable, bufferToStream, decodeBase64, encodeBase64, newWriteableBufferStream, readableToBuffer, streamToBuffer, VSBuffer } from 'vs/base/common/buffer'; import { peekStream } from 'vs/base/common/stream'; suite('Buffer', () => { @@ -478,22 +477,4 @@ suite('Buffer', () => { assert.throws(() => decodeBase64('invalid!')); }); }); - - suite('Float32Array', () => { - - test('serialization', () => { - const array = new Float32Array(10); - for (let i = 0; i < array.length; i++) { - array[i] = i === 0 ? 0 : Math.random(); - } - - const buffer = VSFloat32Array.wrap(array); - const serialized = stringify(buffer); - const deserialized = parse(serialized); - - assert.ok(deserialized instanceof VSFloat32Array); - assert.deepStrictEqual(array, deserialized.buffer); - assert.deepStrictEqual(array.byteLength, deserialized.byteLength); - }); - }); }); diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 7092cc2b8ec..85559f93147 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable local/code-layering, local/code-import-patterns */ import { hostname, release } from 'os'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; diff --git a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts index 3accb99ed0a..34f7c9add07 100644 --- a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts @@ -3,18 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSFloat32Array } from 'vs/base/common/buffer'; +import { VSBuffer } from 'vs/base/common/buffer'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); -export interface IAudioBuffer { - readonly sampleRate: 16000; - readonly sampleSize: 16; - readonly channelCount: 1; - readonly channelData: VSFloat32Array; -} - export interface IVoiceRecognitionService { readonly _serviceBrand: undefined; @@ -24,8 +17,10 @@ export interface IVoiceRecognitionService { * transcribe the spoken words into text. * * @param buffer the audio data obtained from - * the microphone as PCM 32-bit float mono in - * 16khz. + * the microphone as uncompressed PCM data: + * - 1 channel (mono) + * - 16khz sampling rate + * - 16bit sample size */ - transcribe(buffer: IAudioBuffer): Promise; + transcribe(buffer: VSBuffer): Promise; } diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index a9c750ec97d..5cc49cae3ab 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { VSBuffer } from 'vs/base/common/buffer'; import { ILogService } from 'vs/platform/log/common/log'; -import { IAudioBuffer, IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; +import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; export class VoiceRecognitionService implements IVoiceRecognitionService { @@ -14,29 +15,50 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { @ILogService private readonly logService: ILogService ) { } - async transcribe(buffer: IAudioBuffer): Promise { - const now = Date.now(); - this.logService.info(`[voice] transcribe(${buffer.channelData.buffer.length}): Begin`); + async transcribe(buffer: VSBuffer): Promise { + this.logService.info(`[voice] transcribe(${buffer.buffer.length / 4}): Begin`); const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; if (!modulePath) { throw new Error('Voice recognition not yet supported!'); } + const now = Date.now(); + const channelData = this.toFloat32Array(buffer); + const conversionTime = Date.now() - now; + const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; sampleRate: 16000; sampleSize: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); const text = await voiceModule.transcribe({ - sampleRate: buffer.sampleRate, - sampleSize: buffer.sampleSize, - channelCount: buffer.channelCount, - channelData: buffer.channelData.buffer + sampleRate: 16000, + sampleSize: 16, + channelCount: 1, + channelData }, { language: 'en', suppressNonSpeechTokens: true }); - this.logService.info(`[voice] transcribe(${buffer.channelData.buffer.length}): End (text: "${text}", took: ${Date.now() - now}ms))`); + this.logService.info(`[voice] transcribe(${buffer.buffer.length / 4}): End (text: "${text}", took: ${Date.now() - now}ms total, ${conversionTime}ms uint8->float32 conversion)`); return text; } + + private toFloat32Array({ buffer: uint8Array }: VSBuffer): Float32Array { + const float32Array = new Float32Array(uint8Array.length / 4); + let offset = 0; + + for (let i = 0; i < float32Array.length; i++) { + const buffer = new ArrayBuffer(4); + const view = new DataView(buffer); + + for (let j = 0; j < 4; j++) { + view.setUint8(j, uint8Array[offset++]); + } + + float32Array[i] = view.getFloat32(0, true); + } + + return float32Array; + } } diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js index bed2b6ea63c..bc87ec89076 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js @@ -16,8 +16,8 @@ class BufferInputAudioProcessor extends AudioWorkletProcessor { this.bufferTimespan = 4000; this.startTime = undefined; - this.allInputChannelDataBuffer = undefined; - this.currentInputChannelDataBuffer = []; // buffer over the duration of bufferTimespan + this.allInputUint8Array = undefined; + this.currentInputUint8Arrays = []; // buffer over the duration of bufferTimespan } /** @@ -29,16 +29,16 @@ class BufferInputAudioProcessor extends AudioWorkletProcessor { } const inputChannelData = inputs[0][0]; - this.currentInputChannelDataBuffer.push(inputChannelData.slice(0)); + this.currentInputUint8Arrays.push(this.float32ArrayToUint8Array(inputChannelData.slice(0))); if (Date.now() - this.startTime > this.bufferTimespan) { - const currentInputChannelDataBuffer = this.currentInputChannelDataBuffer; - this.currentInputChannelDataBuffer = []; + const currentInputUint8Arrays = this.currentInputUint8Arrays; + this.currentInputUint8Arrays = []; - this.allInputChannelDataBuffer = this._joinFloat32Arrays(this.allInputChannelDataBuffer ? [this.allInputChannelDataBuffer, ...currentInputChannelDataBuffer] : currentInputChannelDataBuffer); + this.allInputUint8Array = this.joinUint8Arrays(this.allInputUint8Array ? [this.allInputUint8Array, ...currentInputUint8Arrays] : currentInputUint8Arrays); // @ts-ignore - this.port.postMessage(this.allInputChannelDataBuffer); + this.port.postMessage(this.allInputUint8Array); this.startTime = Date.now(); } @@ -47,20 +47,42 @@ class BufferInputAudioProcessor extends AudioWorkletProcessor { } /** - * @param {Float32Array[]} float32Arrays - * @returns {Float32Array} + * @param {Uint8Array[]} uint8Arrays + * @returns {Uint8Array} */ - _joinFloat32Arrays(float32Arrays) { - const result = new Float32Array(float32Arrays.reduce((acc, curr) => acc + curr.length, 0)); + joinUint8Arrays(uint8Arrays) { + const result = new Uint8Array(uint8Arrays.reduce((acc, curr) => acc + curr.length, 0)); let offset = 0; - for (const float32Array of float32Arrays) { - result.set(float32Array, offset); - offset += float32Array.length; + for (const uint8Array of uint8Arrays) { + result.set(uint8Array, offset); + offset += uint8Array.length; } return result; } + + /** + * + * @param {Float32Array} float32Array + * @returns {Uint8Array} + */ + float32ArrayToUint8Array(float32Array) { + const uint8Array = new Uint8Array(float32Array.length * 4); + let offset = 0; + + for (let i = 0; i < float32Array.length; i++) { + const buffer = new ArrayBuffer(4); + const view = new DataView(buffer); + view.setFloat32(0, float32Array[i], true); + + for (let j = 0; j < 4; j++) { + uint8Array[offset++] = view.getUint8(j); + } + } + + return uint8Array; + } } // @ts-ignore diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 1e3c986de15..11dfe1c3d33 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { VSFloat32Array } from 'vs/base/common/buffer'; +import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -36,12 +36,12 @@ class BufferInputAudioNode extends AudioWorkletNode { } // TODO@voice -// - load `navigator.mediaDevices.getUserMedia` lazily on startup? or would it trigger a permission prompt? // - how to prevent data processing accumulation when processing is slow? // - how to make this a singleton service that enables ref-counting on multiple callers? // - cancellation should flow to the shared process // - voice module should directly transcribe the PCM32 data -// - we should transfer the Float32Array directly without serialisation overhead +// - we should transfer the Float32Array directly without serialisation overhead maybe from AudioWorklet? +// - the audio worklet should be a TS file (try without any import/export?) export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { @@ -115,7 +115,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); bufferInputAudioTarget.port.onmessage = async e => { - if (e.data instanceof Float32Array) { + if (e.data instanceof Uint8Array) { this.doTranscribeChunk(e.data, emitter, token); } }; @@ -124,18 +124,12 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit }); } - private async doTranscribeChunk(data: Float32Array, emitter: Emitter, token: CancellationToken): Promise { + private async doTranscribeChunk(data: Uint8Array, emitter: Emitter, token: CancellationToken): Promise { if (token.isCancellationRequested) { return; } - const text = await this.voiceRecognitionService.transcribe({ - sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, - sampleSize: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_SIZE, - channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - channelData: VSFloat32Array.wrap(data) - }); - + const text = await this.voiceRecognitionService.transcribe(VSBuffer.wrap(data)); if (token.isCancellationRequested) { return; } From 7e3f23966dd0f5fd7f084837ca359c864a3851f0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 10 Aug 2023 15:43:44 +0200 Subject: [PATCH 013/607] voice - prevent wrong data assumptions in processor --- .../electron-sandbox/bufferInputAudioProcessor.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js index bc87ec89076..f1473538c68 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js @@ -29,6 +29,10 @@ class BufferInputAudioProcessor extends AudioWorkletProcessor { } const inputChannelData = inputs[0][0]; + if ((!(inputChannelData instanceof Float32Array))) { + return; + } + this.currentInputUint8Arrays.push(this.float32ArrayToUint8Array(inputChannelData.slice(0))); if (Date.now() - this.startTime > this.bufferTimespan) { From 64a9f20ae450a99c7714cc75704efc606abb67dd Mon Sep 17 00:00:00 2001 From: weartist Date: Fri, 11 Aug 2023 09:18:41 +0800 Subject: [PATCH 014/607] =?UTF-8?q?=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../browser/widget/diffEditorWidget2/unchangedRanges.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 60f03b220a5..1678761efce 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -189,8 +189,6 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { h('div.bottom@bottom', { title: localize('diff.bottom', 'Click or drag to show more below'), role: 'button' }), ]); - private showAll() { this._unchangedRegion.showAll(undefined); } - constructor( private readonly _editor: ICodeEditor, _viewZone: PlaceholderViewZone, @@ -296,7 +294,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { const lineCount = _unchangedRegion.getHiddenModifiedRange(reader).length; const linesHiddenText = localize('hiddenLines', '{0} Hidden Lines', lineCount); const span = $('span', { title: localize('diff.hiddenLines.expandAll', 'Double click to show all unchanged region') }, linesHiddenText); - addDisposableListener(span, 'dblclick', e => { + span.addEventListener('dblclick', e => { if (e.button !== 0) { return; } @@ -321,4 +319,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { })); } + + private showAll() { this._unchangedRegion.showAll(undefined); } + } From 286530af074343de1a5ce906613dc6f61e51f51f Mon Sep 17 00:00:00 2001 From: Antonio Date: Sat, 12 Aug 2023 00:28:32 +0300 Subject: [PATCH 015/607] fix: add missing pricing parameter to manifest schema --- .../services/extensions/common/extensionsRegistry.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 0df7ff16687..743f0a688d0 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -569,6 +569,12 @@ export const schema: IJSONSchema = { '{Locked="vscode.l10n API"}' ] }, 'The relative path to a folder containing localization (bundle.l10n.*.json) files. Must be specified if you are using the vscode.l10n API.') + }, + pricing: { + type: 'string', + markdownDescription: nls.localize('vscode.extension.pricing', 'The pricing information for the extension. Can be Free (default) or Trial. For more details visit: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#extension-pricing-label'), + enum: ['Free', 'Trial'], + default: 'Free' } } }; From 36b8f6d0375b0fe5cf1d842e2ef9f8949af367b9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 14 Aug 2023 15:23:29 -0700 Subject: [PATCH 016/607] inline chat: fix leaks found in testing Fixes some leaks I found while looking at https://github.com/microsoft/vscode/pull/190444, by adding ensureNoDisposablesAreLeakedInTestSuite to the suite. I don't have a dev setup for inline chat, so I have not tested this beyond running tests and verifying the fix --- src/vs/base/browser/ui/toolbar/toolbar.ts | 1 + .../diffEditorWidget2/diffEditorEditors.ts | 4 +-- .../suggest/browser/suggestController.ts | 2 +- src/vs/platform/actions/browser/buttonbar.ts | 2 +- .../browser/inlineChatController.ts | 3 +- .../inlineChat/browser/inlineChatSession.ts | 2 ++ .../inlineChat/browser/inlineChatWidget.ts | 8 ++--- .../test/browser/inlineChatController.test.ts | 30 +++++++------------ .../markers/test/browser/markersModel.test.ts | 3 ++ 9 files changed, 26 insertions(+), 29 deletions(-) diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 1a6089b5958..67a2401ecfe 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -212,6 +212,7 @@ export class ToolBar extends Disposable { override dispose(): void { this.clear(); + this.disposables.dispose(); super.dispose(); } } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index cce99a94271..ee764c81aed 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -35,8 +35,8 @@ export class DiffEditorEditors extends Disposable { ) { super(); - this.original = this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {}); - this.modified = this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {}); + this.original = this._register(this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {})); + this.modified = this._register(this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {})); this._register(autorunHandleChanges({ createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions), diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 887fb23cb2d..6450e989bce 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -143,7 +143,7 @@ export class SuggestController implements IEditorContribution { // context key: update insert/replace mode const ctxInsertMode = SuggestContext.InsertMode.bindTo(_contextKeyService); ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode); - this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode)); + this._toDispose.add(this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode))); this.widget = this._toDispose.add(new IdleValue(() => { diff --git a/src/vs/platform/actions/browser/buttonbar.ts b/src/vs/platform/actions/browser/buttonbar.ts index cb6ab4c31df..4965d6bb5cd 100644 --- a/src/vs/platform/actions/browser/buttonbar.ts +++ b/src/vs/platform/actions/browser/buttonbar.ts @@ -55,7 +55,7 @@ export class MenuWorkbenchButtonBar extends ButtonBar { 'workbenchActionExecuted', { id: e.action.id, from: options.telemetrySource! } ); - }, this._store); + }, undefined, this._store); } const conifgProvider: IButtonConfigProvider = options?.buttonConfigProvider ?? (() => ({ showLabel: true })); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 104830cc852..f91de7e3ee8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -100,7 +100,7 @@ export class InlineChatController implements IEditorContribution { private _messages = this._store.add(new Emitter()); - private readonly _sessionStore: DisposableStore = new DisposableStore(); + private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); private readonly _stashedSession: MutableDisposable = this._store.add(new MutableDisposable()); private _activeSession?: Session; private _strategy?: EditModeStrategy; @@ -146,6 +146,7 @@ export class InlineChatController implements IEditorContribution { } dispose(): void { + this._strategy?.dispose(); this._stashedSession.clear(); this.finishExistingSession(); this._store.dispose(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 434aea1f4d2..fcbb998dc79 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -385,6 +385,8 @@ export interface IInlineChatSessionService { // recordings(): readonly Recording[]; + + dispose(): void; } type SessionData = { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index bf16c0aec05..8619088ce96 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -230,7 +230,7 @@ export class InlineChatWidget { })); const uri = URI.from({ scheme: 'vscode', authority: 'inline-chat', path: `/inline-chat/model${InlineChatWidget._modelPool++}.txt` }); - this._inputModel = this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri); + this._inputModel = this._store.add(this._modelService.getModel(uri) ?? this._modelService.createModel('', null, uri)); this._inputEditor.setModel(this._inputModel); // --- context keys @@ -359,13 +359,13 @@ export class InlineChatWidget { this._store.add(feedbackToolbar); // preview editors - this._previewDiffEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { + this._previewDiffEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { ..._previewEditorEditorOptions, onlyShowAccessibleDiffViewer: this._accessibilityService.isScreenReaderOptimized(), - }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor))); + }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor)))); this._previewCreateTitle = this._store.add(_instantiationService.createInstance(ResourceLabel, this._elements.previewCreateTitle, { supportIcons: true })); - this._previewCreateEditor = new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor))); + this._previewCreateEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedCodeEditorWidget, this._elements.previewCreate, _previewEditorEditorOptions, codeEditorWidgetOptions, parentEditor)))); this._elements.message.tabIndex = 0; this._elements.message.ariaLabel = this._accessibleViewService.getOpenAriaHint(AccessibilityVerbositySettingId.InlineChat); diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index ab5e4b27e1b..476dfbe33db 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -24,11 +24,11 @@ import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/co import { mock } from 'vs/base/test/common/mock'; import { Emitter, Event } from 'vs/base/common/event'; import { equals } from 'vs/base/common/arrays'; -import { timeout } from 'vs/base/common/async'; import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('InteractiveChatController', function () { @@ -114,11 +114,11 @@ suite('InteractiveChatController', function () { }] ); - instaService = workbenchInstantiationService(undefined, store).createChild(serviceCollection); - inlineChatSessionService = instaService.get(IInlineChatSessionService); + instaService = store.add(workbenchInstantiationService(undefined, store).createChild(serviceCollection)); + inlineChatSessionService = store.add(instaService.get(IInlineChatSessionService)); - model = instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null); - editor = instantiateTestCodeEditor(instaService, model); + model = store.add(instaService.get(IModelService).createModel('Hello\nWorld\nHello Again\nHello World\n', null)); + editor = store.add(instantiateTestCodeEditor(instaService, model)); store.add(inlineChatService.addProvider({ debugName: 'Unit Test', @@ -142,13 +142,14 @@ suite('InteractiveChatController', function () { }); teardown(function () { - editor.dispose(); - model.dispose(); store.clear(); ctrl?.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('creation, not showing anything', function () { + for (let deadline = Date.now() + 1000; Date.now() < deadline;) { } ctrl = instaService.createInstance(TestController, editor); assert.ok(ctrl); assert.strictEqual(ctrl.getWidgetPosition(), undefined); @@ -295,19 +296,8 @@ suite('InteractiveChatController', function () { wholeRange: new Range(3, 1, 3, 3) }; }, - async provideResponse(session, request) { - - // SLOW response - await timeout(50000); - - return { - type: InlineChatResponseType.EditorEdit, - id: Math.random(), - edits: [{ - range: new Range(1, 1, 1, 1), // EDIT happens outside of whole range - text: `${request.prompt}\n${request.prompt}` - }] - }; + provideResponse(session, request) { + return new Promise(() => { }); } }); store.add(d); diff --git a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts index 9cb2c9dc650..b8334c948d6 100644 --- a/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts +++ b/src/vs/workbench/contrib/markers/test/browser/markersModel.test.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { IMarker, MarkerSeverity, IRelatedInformation } from 'vs/platform/markers/common/markers'; import { MarkersModel, Marker, ResourceMarkers, RelatedInformation } from 'vs/workbench/contrib/markers/browser/markersModel'; import { groupBy } from 'vs/base/common/collections'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestMarkersModel extends MarkersModel { @@ -27,6 +28,8 @@ class TestMarkersModel extends MarkersModel { suite('MarkersModel Test', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('marker ids are unique', function () { const marker1 = anErrorWithRange(3); const marker2 = anErrorWithRange(3); From b4801aeb02735469083b40d00aafe7dbff075f62 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 15 Aug 2023 12:20:02 +0200 Subject: [PATCH 017/607] adding the data relative to the diagnostic error codes in the typescript extension --- .../src/languageFeatures/diagnostics.ts | 28 ++++++++++++++++++- .../src/typescriptServiceClient.ts | 3 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index aeb4491872e..5981e63ea6e 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -8,6 +8,7 @@ import { DiagnosticLanguage } from '../configuration/languageDescription'; import * as arrays from '../utils/arrays'; import { Disposable } from '../utils/dispose'; import { ResourceMap } from '../utils/resourceMap'; +import { TelemetryReporter } from '../logging/telemetry'; function diagnosticsEquals(a: vscode.Diagnostic, b: vscode.Diagnostic): boolean { if (a === b) { @@ -153,11 +154,13 @@ export class DiagnosticsManager extends Disposable { private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; private readonly _pendingUpdates: ResourceMap; + private readonly _telemetryReporter: TelemetryReporter; private readonly _updateDelay = 50; constructor( owner: string, + telemetryReporter: TelemetryReporter, onCaseInsensitiveFileSystem: boolean ) { super(); @@ -165,6 +168,7 @@ export class DiagnosticsManager extends Disposable { this._pendingUpdates = new ResourceMap(undefined, { onCaseInsensitiveFileSystem }); this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); + this._telemetryReporter = telemetryReporter; } public override dispose() { @@ -238,7 +242,29 @@ export class DiagnosticsManager extends Disposable { } public getDiagnostics(file: vscode.Uri): ReadonlyArray { - return this._currentDiagnostics.get(file) || []; + const diagnostics = this._currentDiagnostics.get(file) || []; + const diagnoticCodes = diagnostics.reduce(function (result: number[], d: vscode.Diagnostic) { + const code = d.code; + if (typeof code === 'string' || typeof code === 'number') { + result.push(Number(code)); + } else if (code !== undefined) { + result.push(Number(code.value)); + } + return result; + }, []).sort(); + /* __GDPR__ + "typescript.diagnostics" : { + "owner": "@aiday-mar", + "diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this._telemetryReporter.logTelemetry('typescript.diagnostics', { + diagnoticCodes: diagnoticCodes.join(', ') + }); + return diagnostics; } private scheduleDiagnosticsUpdate(file: vscode.Uri) { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 86c6bb8d9f1..c165f82db40 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -176,7 +176,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds, onCaseInsenitiveFileSystem); this.onReady(() => { this.bufferSyncSupport.listen(); }); - this.diagnosticsManager = new DiagnosticsManager('typescript', onCaseInsenitiveFileSystem); this.bufferSyncSupport.onDelete(resource => { this.cancelInflightRequestsForResource(resource); this.diagnosticsManager.deleteAllDiagnosticsInFile(resource); @@ -213,7 +212,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } return this.apiVersion.fullVersionString; }); - + this.diagnosticsManager = new DiagnosticsManager('typescript', this.telemetryReporter, onCaseInsenitiveFileSystem); this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory); this._register(this.pluginManager.onDidUpdateConfig(update => { From ea816abae3b66dade07e554107d52d822bf1b63a Mon Sep 17 00:00:00 2001 From: Lucas Marioza Date: Tue, 15 Aug 2023 12:58:40 -0300 Subject: [PATCH 018/607] Ensure style element is added to shadowDOM components on colorizeElement --- src/vs/editor/standalone/browser/standaloneEditor.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 4ff53468f06..005e48ad530 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -352,8 +352,9 @@ export function createWebWorker(opts: IWebWorkerOptions): Mona export function colorizeElement(domNode: HTMLElement, options: IColorizerElementOptions): Promise { const languageService = StandaloneServices.get(ILanguageService); const themeService = StandaloneServices.get(IStandaloneThemeService); - themeService.registerEditorContainer(domNode); - return Colorizer.colorizeElement(themeService, languageService, domNode, options); + return Colorizer.colorizeElement(themeService, languageService, domNode, options).then(() => { + themeService.registerEditorContainer(domNode); + }); } /** From d73f48a3470cbbb25533d8fae122411f1024383f Mon Sep 17 00:00:00 2001 From: Neelesh Bodas Date: Wed, 16 Aug 2023 12:10:49 -0700 Subject: [PATCH 019/607] Adding empty alt text to decorative images for extensions --- src/vs/workbench/contrib/extensions/browser/extensionEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 366c4d28382..b2c9cbcdf50 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -276,7 +276,7 @@ export class ExtensionEditor extends EditorPane { const header = append(root, $('.header')); const iconContainer = append(header, $('.icon-container')); - const icon = append(iconContainer, $('img.icon', { draggable: false })); + const icon = append(iconContainer, $('img.icon', { draggable: false, alt: '' })); const remoteBadge = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer, true); const details = append(header, $('.details')); From 0b4911870f55568d2c5ff83a48630eb8d395c6fe Mon Sep 17 00:00:00 2001 From: Neelesh Bodas Date: Wed, 16 Aug 2023 12:16:39 -0700 Subject: [PATCH 020/607] Remove incorrect role from the title bar. --- src/vs/workbench/browser/workbench.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 28863e8e434..8fae627d8f6 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -342,7 +342,7 @@ export class Workbench extends Layout { // Create Parts for (const { id, role, classes, options } of [ - { id: Parts.TITLEBAR_PART, role: 'contentinfo', classes: ['titlebar'] }, + { id: Parts.TITLEBAR_PART, role: 'none', classes: ['titlebar'] }, { id: Parts.BANNER_PART, role: 'banner', classes: ['banner'] }, { id: Parts.ACTIVITYBAR_PART, role: 'none', classes: ['activitybar', this.getSideBarPosition() === Position.LEFT ? 'left' : 'right'] }, // Use role 'none' for some parts to make screen readers less chatty #114892 { id: Parts.SIDEBAR_PART, role: 'none', classes: ['sidebar', this.getSideBarPosition() === Position.LEFT ? 'left' : 'right'] }, From b96621b58c0ec2cfad8e848712d11f8c5bf30f84 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2023 11:47:14 +0200 Subject: [PATCH 021/607] voice - implement direct `MessagePort` communcation between audio worklet and shared process --- build/gulpfile.vscode.js | 2 +- src/vs/base/parts/ipc/node/ipc.mp.ts | 25 ++++- .../sharedProcess/contrib/voiceTranscriber.ts | 40 ++++++++ .../node/sharedProcess/sharedProcessMain.ts | 49 +++++++--- .../platform/ipc/electron-sandbox/services.ts | 14 +++ .../sharedProcess/common/sharedProcess.ts | 20 ++++ .../electron-main/sharedProcess.ts | 34 ++++--- .../common/voiceRecognitionService.ts | 7 +- .../node/voiceRecognitionService.ts | 26 +----- .../electron-sandbox/sharedProcessService.ts | 16 +++- .../bufferInputAudioProcessor.js | 93 ------------------- .../bufferedVoiceTranscriber.js | 92 ++++++++++++++++++ .../voiceRecognitionService.ts | 9 -- .../workbenchVoiceRecognitionService.ts | 80 ++++++++-------- .../electron-sandbox/workbenchTestServices.ts | 2 + src/vs/workbench/workbench.desktop.main.ts | 1 - 16 files changed, 309 insertions(+), 201 deletions(-) create mode 100644 src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts create mode 100644 src/vs/platform/sharedProcess/common/sharedProcess.ts delete mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js create mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js delete mode 100644 src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 2093df79b16..cd0a762dfa7 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -72,7 +72,7 @@ const vscodeResources = [ 'out-build/vs/workbench/contrib/terminal/browser/media/*.sh', 'out-build/vs/workbench/contrib/terminal/browser/media/*.zsh', 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', - 'out-build/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js', + 'out-build/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', '!**/test/**' diff --git a/src/vs/base/parts/ipc/node/ipc.mp.ts b/src/vs/base/parts/ipc/node/ipc.mp.ts index a7cfc538d5e..1a8b107dc99 100644 --- a/src/vs/base/parts/ipc/node/ipc.mp.ts +++ b/src/vs/base/parts/ipc/node/ipc.mp.ts @@ -33,18 +33,35 @@ class Protocol implements IMessagePassingProtocol { } } +export interface IClientConnectionFilter { + + /** + * Allows to filter incoming messages to the + * server to handle them differently. + * + * @param e the message event to handle + * @returns `true` if the event was handled + * and should not be processed by the server. + */ + handled(e: MessageEvent): boolean; +} + /** * An implementation of a `IPCServer` on top of MessagePort style IPC communication. * The clients register themselves via Electron Utility Process IPC transfer. */ export class Server extends IPCServer { - private static getOnDidClientConnect(): Event { + private static getOnDidClientConnect(filter?: IClientConnectionFilter): Event { assertType(isUtilityProcess(process), 'Electron Utility Process'); const onCreateMessageChannel = new Emitter(); - process.parentPort.on('message', (e: Electron.MessageEvent) => { + process.parentPort.on('message', (e: MessageEvent) => { + if (filter?.handled(e)) { + return; + } + const port = firstOrDefault(e.ports); if (port) { onCreateMessageChannel.fire(port); @@ -66,8 +83,8 @@ export class Server extends IPCServer { }); } - constructor() { - super(Server.getOnDidClientConnect()); + constructor(filter?: IClientConnectionFilter) { + super(Server.getOnDidClientConnect(filter)); } } diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts new file mode 100644 index 00000000000..cbd23de96c4 --- /dev/null +++ b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { MessagePortMain, MessageEvent } from 'vs/base/parts/sandbox/node/electronTypes'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; + +export class VoiceTranscriber extends Disposable { + + constructor( + private readonly onDidWindowConnectRaw: Event, + @IVoiceRecognitionService private readonly voiceRecognitionService: IVoiceRecognitionService, + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + this._register(this.onDidWindowConnectRaw(port => { + const portHandler = async (e: MessageEvent) => { + if (!(e.data instanceof Float32Array)) { + return; + } + + const result = await this.voiceRecognitionService.transcribe(e.data); + + port.postMessage(result); + }; + + port.on('message', portHandler); + this._register(toDisposable(() => port.off('message', portHandler))); + + port.start(); + })); + } +} diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 85559f93147..91e93fccc59 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -4,13 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { hostname, release } from 'os'; +import { MessagePortMain, MessageEvent } from 'vs/base/parts/sandbox/node/electronTypes'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { combinedDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { firstOrDefault } from 'vs/base/common/arrays'; +import { Emitter } from 'vs/base/common/event'; import { ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; -import { Server as UtilityProcessMessagePortServer, once } from 'vs/base/parts/ipc/node/ipc.mp'; +import { IClientConnectionFilter, Server as UtilityProcessMessagePortServer, once } from 'vs/base/parts/ipc/node/ipc.mp'; import { CodeCacheCleaner } from 'vs/code/node/sharedProcess/contrib/codeCacheCleaner'; import { LanguagePackCachedDataCleaner } from 'vs/code/node/sharedProcess/contrib/languagePackCachedDataCleaner'; import { LocalizationsUpdater } from 'vs/code/node/sharedProcess/contrib/localizationsUpdater'; @@ -113,13 +116,17 @@ import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; import { VoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; +import { VoiceTranscriber } from 'vs/code/node/sharedProcess/contrib/voiceTranscriber'; +import { RawSharedProcessConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; -class SharedProcessMain extends Disposable { +class SharedProcessMain extends Disposable implements IClientConnectionFilter { - private readonly server = this._register(new UtilityProcessMessagePortServer()); + private readonly server = this._register(new UtilityProcessMessagePortServer(this)); private lifecycleService: SharedProcessLifecycleService | undefined = undefined; + private readonly onDidWindowConnectRaw = this._register(new Emitter()); + constructor(private configuration: ISharedProcessConfiguration) { super(); @@ -139,7 +146,7 @@ class SharedProcessMain extends Disposable { } }; process.once('exit', onExit); - once(process.parentPort, 'vscode:electron-main->shared-process=exit', onExit); + once(process.parentPort, SharedProcessLifecycle.exit, onExit); } async init(): Promise { @@ -171,7 +178,8 @@ class SharedProcessMain extends Disposable { instantiationService.createInstance(LogsDataCleaner), instantiationService.createInstance(LocalizationsUpdater), instantiationService.createInstance(ExtensionsContributions), - instantiationService.createInstance(UserDataProfilesCleaner) + instantiationService.createInstance(UserDataProfilesCleaner), + instantiationService.createInstance(VoiceTranscriber, this.onDidWindowConnectRaw.event) )); } @@ -353,7 +361,7 @@ class SharedProcessMain extends Disposable { services.set(IRemoteTunnelService, new SyncDescriptor(RemoteTunnelService)); // Voice Recognition - services.set(IVoiceRecognitionService, new SyncDescriptor(VoiceRecognitionService, undefined, false /* proxied to other processes */)); + services.set(IVoiceRecognitionService, new SyncDescriptor(VoiceRecognitionService)); return new InstantiationService(services); } @@ -412,10 +420,6 @@ class SharedProcessMain extends Disposable { // Remote Tunnel const remoteTunnelChannel = ProxyChannel.fromService(accessor.get(IRemoteTunnelService)); this.server.registerChannel('remoteTunnel', remoteTunnelChannel); - - // Voice Recognition - const voiceRecognitionChannel = ProxyChannel.fromService(accessor.get(IVoiceRecognitionService)); - this.server.registerChannel('voiceRecognition', voiceRecognitionChannel); } private registerErrorHandler(logService: ILogService): void { @@ -434,6 +438,27 @@ class SharedProcessMain extends Disposable { logService.error(`[uncaught exception in sharedProcess]: ${message}`); }); } + + handled(e: MessageEvent): boolean { + + // This filter on message port messages will look for + // attempts of a window to connect raw to the shared + // process to handle these connections separate from + // our IPC based protocol. + + if (e.data !== RawSharedProcessConnection.response) { + return false; + } + + const port = firstOrDefault(e.ports); + if (port) { + this.onDidWindowConnectRaw.fire(port); + + return true; + } + + return false; + } } export async function main(configuration: ISharedProcessConfiguration): Promise { @@ -442,12 +467,12 @@ export async function main(configuration: ISharedProcessConfiguration): Promise< // ready to accept message ports as client connections const sharedProcess = new SharedProcessMain(configuration); - process.parentPort.postMessage('vscode:shared-process->electron-main=ipc-ready'); + process.parentPort.postMessage(SharedProcessLifecycle.ipcReady); // await initialization and signal this back to electron-main await sharedProcess.init(); - process.parentPort.postMessage('vscode:shared-process->electron-main=init-done'); + process.parentPort.postMessage(SharedProcessLifecycle.initDone); } process.parentPort.once('message', (e: Electron.MessageEvent) => { diff --git a/src/vs/platform/ipc/electron-sandbox/services.ts b/src/vs/platform/ipc/electron-sandbox/services.ts index 36921e83ef5..060ef30d96d 100644 --- a/src/vs/platform/ipc/electron-sandbox/services.ts +++ b/src/vs/platform/ipc/electron-sandbox/services.ts @@ -63,6 +63,20 @@ export function registerMainProcessRemoteService(id: ServiceIdentifier, ch export const ISharedProcessService = createDecorator('sharedProcessService'); export interface ISharedProcessService extends IRemoteService { + + /** + * Allows to create a `MessagePort` connection between the + * shared process and the renderer process. + * + * Use this only when you need raw IPC to the shared process + * via `postMessage` and `on('message')` of special data structures + * like typed arrays. + * + * Callers have to call `port.start()` after having installed + * listeners to enable the data flow. + */ + createRawConnection(): Promise; + notifyRestored(): void; } diff --git a/src/vs/platform/sharedProcess/common/sharedProcess.ts b/src/vs/platform/sharedProcess/common/sharedProcess.ts new file mode 100644 index 00000000000..7e443fffb12 --- /dev/null +++ b/src/vs/platform/sharedProcess/common/sharedProcess.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const SharedProcessLifecycle = { + exit: 'vscode:electron-main->shared-process=exit', + ipcReady: 'vscode:shared-process->electron-main=ipc-ready', + initDone: 'vscode:shared-process->electron-main=init-done' +}; + +export const ChannelSharedProcessConnection = { + request: 'vscode:createChannelSharedProcessConnection', + response: 'vscode:createChannelSharedProcessConnectionResult' +}; + +export const RawSharedProcessConnection = { + request: 'vscode:createRawSharedProcessConnection', + response: 'vscode:createRawSharedProcessConnectionResult' +}; diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 51dd9d611a3..ec41ba8cc7a 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -18,6 +18,7 @@ import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utility import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { parseSharedProcessDebugPort } from 'vs/platform/environment/node/environmentService'; import { assertIsDefined } from 'vs/base/common/types'; +import { ChannelSharedProcessConnection, RawSharedProcessConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; export class SharedProcess extends Disposable { @@ -41,15 +42,18 @@ export class SharedProcess extends Disposable { private registerListeners(): void { - // Shared process connections from workbench windows - validatedIpcMain.on('vscode:createSharedProcessMessageChannel', (e, nonce: string) => this.onWindowConnection(e, nonce)); + // Shared process channel connections from workbench windows + validatedIpcMain.on(ChannelSharedProcessConnection.request, (e, nonce: string) => this.onWindowConnection(e, nonce, ChannelSharedProcessConnection.response)); + + // Shared process raw connections from workbench windows + validatedIpcMain.on(RawSharedProcessConnection.request, (e, nonce: string) => this.onWindowConnection(e, nonce, RawSharedProcessConnection.response)); // Lifecycle this._register(this.lifecycleMainService.onWillShutdown(() => this.onWillShutdown())); } - private async onWindowConnection(e: IpcMainEvent, nonce: string): Promise { - this.logService.trace('[SharedProcess] on vscode:createSharedProcessMessageChannel'); + private async onWindowConnection(e: IpcMainEvent, nonce: string, responseChannel: string): Promise { + this.logService.trace(`[SharedProcess] onWindowConnection for: ${responseChannel}`); // release barrier if this is the first window connection if (!this.firstWindowConnectionBarrier.isOpen()) { @@ -62,8 +66,10 @@ export class SharedProcess extends Disposable { await this.whenReady(); - // connect to the shared process - const port = await this.connect(); + // connect to the shared process passing the responseChannel + // as payload to give a hint what the connection is about + + const port = await this.connect(responseChannel); // Check back if the requesting window meanwhile closed // Since shared process is delayed on startup there is @@ -75,13 +81,13 @@ export class SharedProcess extends Disposable { } // send the port back to the requesting window - e.sender.postMessage('vscode:createSharedProcessMessageChannelResult', nonce, [port]); + e.sender.postMessage(responseChannel, nonce, [port]); } private onWillShutdown(): void { this.logService.trace('[SharedProcess] onWillShutdown'); - this.utilityProcess?.postMessage('vscode:electron-main->shared-process=exit'); + this.utilityProcess?.postMessage(SharedProcessLifecycle.exit); this.utilityProcess = undefined; } @@ -98,9 +104,9 @@ export class SharedProcess extends Disposable { const whenReady = new DeferredPromise(); if (this.utilityProcess) { - this.utilityProcess.once('vscode:shared-process->electron-main=init-done', () => whenReady.complete()); + this.utilityProcess.once(SharedProcessLifecycle.initDone, () => whenReady.complete()); } else { - validatedIpcMain.once('vscode:shared-process->electron-main=init-done', () => whenReady.complete()); + validatedIpcMain.once(SharedProcessLifecycle.initDone, () => whenReady.complete()); } await whenReady.p; @@ -125,9 +131,9 @@ export class SharedProcess extends Disposable { // Wait for shared process indicating that IPC connections are accepted const sharedProcessIpcReady = new DeferredPromise(); if (this.utilityProcess) { - this.utilityProcess.once('vscode:shared-process->electron-main=ipc-ready', () => sharedProcessIpcReady.complete()); + this.utilityProcess.once(SharedProcessLifecycle.ipcReady, () => sharedProcessIpcReady.complete()); } else { - validatedIpcMain.once('vscode:shared-process->electron-main=ipc-ready', () => sharedProcessIpcReady.complete()); + validatedIpcMain.once(SharedProcessLifecycle.ipcReady, () => sharedProcessIpcReady.complete()); } await sharedProcessIpcReady.p; @@ -175,13 +181,13 @@ export class SharedProcess extends Disposable { }; } - async connect(): Promise { + async connect(payload?: unknown): Promise { // Wait for shared process being ready to accept connection await this.whenIpcReady; // Connect and return message port const utilityProcess = assertIsDefined(this.utilityProcess); - return utilityProcess.connect(); + return utilityProcess.connect(payload); } } diff --git a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts index 34f7c9add07..a563a2e45cd 100644 --- a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSBuffer } from 'vs/base/common/buffer'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); @@ -16,11 +15,11 @@ export interface IVoiceRecognitionService { * Given a buffer of audio data, attempts to * transcribe the spoken words into text. * - * @param buffer the audio data obtained from - * the microphone as uncompressed PCM data: + * @param channelData the raw audio data obtained + * from the microphone as uncompressed PCM data: * - 1 channel (mono) * - 16khz sampling rate * - 16bit sample size */ - transcribe(buffer: VSBuffer): Promise; + transcribe(channelData: Float32Array): Promise; } diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index 5cc49cae3ab..a67fb2c54b4 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSBuffer } from 'vs/base/common/buffer'; import { ILogService } from 'vs/platform/log/common/log'; import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; @@ -15,8 +14,8 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { @ILogService private readonly logService: ILogService ) { } - async transcribe(buffer: VSBuffer): Promise { - this.logService.info(`[voice] transcribe(${buffer.buffer.length / 4}): Begin`); + async transcribe(channelData: Float32Array): Promise { + this.logService.info(`[voice] transcribe(${channelData.length}): Begin`); const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; if (!modulePath) { @@ -24,7 +23,6 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { } const now = Date.now(); - const channelData = this.toFloat32Array(buffer); const conversionTime = Date.now() - now; const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; sampleRate: 16000; sampleSize: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); @@ -39,26 +37,8 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { suppressNonSpeechTokens: true }); - this.logService.info(`[voice] transcribe(${buffer.buffer.length / 4}): End (text: "${text}", took: ${Date.now() - now}ms total, ${conversionTime}ms uint8->float32 conversion)`); + this.logService.info(`[voice] transcribe(${channelData.length}): End (text: "${text}", took: ${Date.now() - now}ms total, ${conversionTime}ms uint8->float32 conversion)`); return text; } - - private toFloat32Array({ buffer: uint8Array }: VSBuffer): Float32Array { - const float32Array = new Float32Array(uint8Array.length / 4); - let offset = 0; - - for (let i = 0; i < float32Array.length; i++) { - const buffer = new ArrayBuffer(4); - const view = new DataView(buffer); - - for (let j = 0; j < 4; j++) { - view.setUint8(j, uint8Array[offset++]); - } - - float32Array[i] = view.getFloat32(0, true); - } - - return float32Array; - } } diff --git a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts index ce810b44b05..11a2fd6a72b 100644 --- a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts +++ b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts @@ -8,6 +8,7 @@ import { IChannel, IServerChannel, getDelayedChannel } from 'vs/base/parts/ipc/c import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; +import { ChannelSharedProcessConnection, RawSharedProcessConnection } from 'vs/platform/sharedProcess/common/sharedProcess'; import { mark } from 'vs/base/common/performance'; import { Barrier, timeout } from 'vs/base/common/async'; import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; @@ -44,7 +45,7 @@ export class SharedProcessService extends Disposable implements ISharedProcessSe // Acquire a message port connected to the shared process mark('code/willConnectSharedProcess'); this.logService.trace('Renderer->SharedProcess#connect: before acquirePort'); - const port = await acquirePort('vscode:createSharedProcessMessageChannel', 'vscode:createSharedProcessMessageChannelResult'); + const port = await acquirePort(ChannelSharedProcessConnection.request, ChannelSharedProcessConnection.response); mark('code/didConnectSharedProcess'); this.logService.trace('Renderer->SharedProcess#connect: connection established'); @@ -64,4 +65,17 @@ export class SharedProcessService extends Disposable implements ISharedProcessSe registerChannel(channelName: string, channel: IServerChannel): void { this.withSharedProcessConnection.then(connection => connection.registerChannel(channelName, channel)); } + + async createRawConnection(): Promise { + + // Await initialization of the shared process + await this.connect(); + + // Create a new port to the shared process + this.logService.trace('Renderer->SharedProcess#createRawConnection: before acquirePort'); + const port = await acquirePort(RawSharedProcessConnection.request, RawSharedProcessConnection.response); + this.logService.trace('Renderer->SharedProcess#createRawConnection: connection established'); + + return port; + } } diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js deleted file mode 100644 index f1473538c68..00000000000 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js +++ /dev/null @@ -1,93 +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 -'use strict'; - -// @ts-ignore -class BufferInputAudioProcessor extends AudioWorkletProcessor { - - constructor() { - super(); - - this.channelCount = 1; - this.bufferTimespan = 4000; - this.startTime = undefined; - - this.allInputUint8Array = undefined; - this.currentInputUint8Arrays = []; // buffer over the duration of bufferTimespan - } - - /** - * @param {[[Float32Array]]} inputs - */ - process(inputs) { - if (this.startTime === undefined) { - this.startTime = Date.now(); - } - - const inputChannelData = inputs[0][0]; - if ((!(inputChannelData instanceof Float32Array))) { - return; - } - - this.currentInputUint8Arrays.push(this.float32ArrayToUint8Array(inputChannelData.slice(0))); - - if (Date.now() - this.startTime > this.bufferTimespan) { - const currentInputUint8Arrays = this.currentInputUint8Arrays; - this.currentInputUint8Arrays = []; - - this.allInputUint8Array = this.joinUint8Arrays(this.allInputUint8Array ? [this.allInputUint8Array, ...currentInputUint8Arrays] : currentInputUint8Arrays); - - // @ts-ignore - this.port.postMessage(this.allInputUint8Array); - - this.startTime = Date.now(); - } - - return true; - } - - /** - * @param {Uint8Array[]} uint8Arrays - * @returns {Uint8Array} - */ - joinUint8Arrays(uint8Arrays) { - const result = new Uint8Array(uint8Arrays.reduce((acc, curr) => acc + curr.length, 0)); - - let offset = 0; - for (const uint8Array of uint8Arrays) { - result.set(uint8Array, offset); - offset += uint8Array.length; - } - - return result; - } - - /** - * - * @param {Float32Array} float32Array - * @returns {Uint8Array} - */ - float32ArrayToUint8Array(float32Array) { - const uint8Array = new Uint8Array(float32Array.length * 4); - let offset = 0; - - for (let i = 0; i < float32Array.length; i++) { - const buffer = new ArrayBuffer(4); - const view = new DataView(buffer); - view.setFloat32(0, float32Array[i], true); - - for (let j = 0; j < 4; j++) { - uint8Array[offset++] = view.getUint8(j); - } - } - - return uint8Array; - } -} - -// @ts-ignore -registerProcessor('buffer-input-audio-processor', BufferInputAudioProcessor); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js new file mode 100644 index 00000000000..3e4d6641c22 --- /dev/null +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +// @ts-ignore +class BufferedVoiceTranscriber extends AudioWorkletProcessor { + + constructor() { + super(); + + this.channelCount = 1; + this.bufferTimespan = 4000; + this.startTime = undefined; + + this.allInputFloat32Array = undefined; + this.currentInputFloat32Arrays = []; // buffer over the duration of bufferTimespan + + this.registerListeners(); + } + + registerListeners() { + + // @ts-ignore + const port = this.port; + port.onmessage = event => { + if (event.data === 'vscode:transferPortToAudioWorklet') { + this.sharedProcessPort = event.ports[0]; + + this.sharedProcessPort.onmessage = event => { + if (typeof event.data === 'string') { + port.postMessage(event.data); + } + }; + + this.sharedProcessPort.start(); + } + }; + } + + /** + * @param {[[Float32Array]]} inputs + */ + process(inputs) { + if (this.startTime === undefined) { + this.startTime = Date.now(); + } + + const inputChannelData = inputs[0][0]; + if ((!(inputChannelData instanceof Float32Array))) { + return; + } + + this.currentInputFloat32Arrays.push(inputChannelData.slice(0)); + + if (Date.now() - this.startTime > this.bufferTimespan && this.sharedProcessPort) { + const currentInputFloat32Arrays = this.currentInputFloat32Arrays; + this.currentInputFloat32Arrays = []; + + this.allInputFloat32Array = this.joinFloat32Arrays(this.allInputFloat32Array ? [this.allInputFloat32Array, ...currentInputFloat32Arrays] : currentInputFloat32Arrays); + + // @ts-ignore + this.sharedProcessPort.postMessage(this.allInputFloat32Array); + + this.startTime = Date.now(); + } + + return true; + } + + /** + * @param {Float32Array[]} float32Arrays + * @returns {Float32Array} + */ + joinFloat32Arrays(float32Arrays) { + const result = new Float32Array(float32Arrays.reduce((acc, curr) => acc + curr.length, 0)); + + let offset = 0; + for (const float32Array of float32Arrays) { + result.set(float32Array, offset); + offset += float32Array.length; + } + + return result; + } +} + +// @ts-ignore +registerProcessor('buffered-voice-transcriber', BufferedVoiceTranscriber); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts deleted file mode 100644 index e8b4e771ffa..00000000000 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService.ts +++ /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. - *--------------------------------------------------------------------------------------------*/ - -import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; -import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; - -registerSharedProcessRemoteService(IVoiceRecognitionService, 'voiceRecognition'); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 11dfe1c3d33..523b07d1066 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -4,15 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; import { Emitter, Event } from 'vs/base/common/event'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { DeferredPromise } from 'vs/base/common/async'; import { FileAccess } from 'vs/base/common/network'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; export const IWorkbenchVoiceRecognitionService = createDecorator('workbenchVoiceRecognitionService'); @@ -29,9 +28,32 @@ export interface IWorkbenchVoiceRecognitionService { transcribe(cancellation: CancellationToken): Event; } -class BufferInputAudioNode extends AudioWorkletNode { - constructor(context: BaseAudioContext, options: AudioWorkletNodeOptions) { - super(context, 'buffer-input-audio-processor', options); +class BufferedVoiceTranscriber extends AudioWorkletNode { + + constructor( + context: BaseAudioContext, + options: AudioWorkletNodeOptions, + private readonly onDidTranscribe: Emitter, + private readonly sharedProcessService: ISharedProcessService + ) { + super(context, 'buffered-voice-transcriber', options); + + this.registerListeners(); + } + + private registerListeners(): void { + this.port.onmessage = e => { + if (typeof e.data === 'string') { + this.onDidTranscribe.fire(e.data); + } + }; + } + + async start(token: CancellationToken): Promise { + const rawSharedProcessConnection = await this.sharedProcessService.createRawConnection(); + token.onCancellationRequested(() => rawSharedProcessConnection.close()); + + this.port.postMessage('vscode:transferPortToAudioWorklet', [rawSharedProcessConnection]); } } @@ -39,8 +61,7 @@ class BufferInputAudioNode extends AudioWorkletNode { // - how to prevent data processing accumulation when processing is slow? // - how to make this a singleton service that enables ref-counting on multiple callers? // - cancellation should flow to the shared process -// - voice module should directly transcribe the PCM32 data -// - we should transfer the Float32Array directly without serialisation overhead maybe from AudioWorklet? +// - voice module should directly transcribe the PCM32 data without wav+file conversion // - the audio worklet should be a TS file (try without any import/export?) export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { @@ -52,21 +73,20 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit private static readonly AUDIO_CHANNELS = 1; constructor( - @IVoiceRecognitionService private readonly voiceRecognitionService: IVoiceRecognitionService, - @IProgressService private readonly progressService: IProgressService + @IProgressService private readonly progressService: IProgressService, + @ISharedProcessService private readonly sharedProcessService: ISharedProcessService ) { } transcribe(cancellation: CancellationToken): Event { - const cts = new CancellationTokenSource(cancellation); - const emitter = new Emitter(); - cancellation.onCancellationRequested(() => emitter.dispose()); + const onDidTranscribe = new Emitter(); + cancellation.onCancellationRequested(() => onDidTranscribe.dispose()); - this.doTranscribe(emitter, cts.token); + this.doTranscribe(onDidTranscribe, cancellation); - return emitter.event; + return onDidTranscribe.event; } - private async doTranscribe(emitter: Emitter, token: CancellationToken): Promise { + private async doTranscribe(onDidTranscribe: Emitter, token: CancellationToken): Promise { return this.progressService.withProgress({ location: ProgressLocation.Window, title: localize('voiceTranscription', "Voice Transcription"), @@ -103,39 +123,21 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit recordingDone.complete(); }); - await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/bufferInputAudioProcessor.js').toString(true)); + await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js').toString(true)); - const bufferInputAudioTarget = new BufferInputAudioNode(audioContext, { + const bufferedVoiceTranscriberTarget = new BufferedVoiceTranscriber(audioContext, { channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, channelCountMode: 'explicit' - }); + }, onDidTranscribe, this.sharedProcessService); + await bufferedVoiceTranscriberTarget.start(token); - microphoneSource.connect(bufferInputAudioTarget); + microphoneSource.connect(bufferedVoiceTranscriberTarget); progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); - bufferInputAudioTarget.port.onmessage = async e => { - if (e.data instanceof Uint8Array) { - this.doTranscribeChunk(e.data, emitter, token); - } - }; - return recordingDone.p; }); } - - private async doTranscribeChunk(data: Uint8Array, emitter: Emitter, token: CancellationToken): Promise { - if (token.isCancellationRequested) { - return; - } - - const text = await this.voiceRecognitionService.transcribe(VSBuffer.wrap(data)); - if (token.isCancellationRequested) { - return; - } - - emitter.fire(text); - } } // Register Service diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index 45fa3e437b7..1a40dc62bd5 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -51,6 +51,8 @@ export class TestSharedProcessService implements ISharedProcessService { declare readonly _serviceBrand: undefined; + createRawConnection(): never { throw new Error('Not Implemented'); } + getChannel(channelName: string): any { return undefined; } registerChannel(channelName: string, channel: any): void { } diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index b3367ac6ea2..1e429387e2d 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -77,7 +77,6 @@ import 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentServi import 'vs/workbench/services/integrity/electron-sandbox/integrityService'; import 'vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService'; import 'vs/workbench/services/checksum/electron-sandbox/checksumService'; -import 'vs/workbench/services/voiceRecognition/electron-sandbox/voiceRecognitionService'; import 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; import 'vs/platform/remote/electron-sandbox/sharedProcessTunnelService'; import 'vs/workbench/services/tunnel/electron-sandbox/tunnelService'; From e36739b66dce75ffbe25711a05c5a4e620a60868 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2023 12:13:14 +0200 Subject: [PATCH 022/607] voice - some cleanup and :lipstick: --- src/vs/base/common/marshallingIds.ts | 3 +-- src/vs/base/parts/ipc/node/ipc.mp.ts | 4 ++-- .../sharedProcess/contrib/voiceTranscriber.ts | 1 + .../code/node/sharedProcess/sharedProcessMain.ts | 6 +++--- .../sharedProcess/common/sharedProcess.ts | 12 ++++++------ .../sharedProcess/electron-main/sharedProcess.ts | 6 +++--- .../electron-sandbox/sharedProcessService.ts | 8 ++++---- .../electron-sandbox/bufferedVoiceTranscriber.js | 15 +++++++-------- .../workbenchVoiceRecognitionService.ts | 6 +++--- 9 files changed, 30 insertions(+), 31 deletions(-) diff --git a/src/vs/base/common/marshallingIds.ts b/src/vs/base/common/marshallingIds.ts index 7cb27d18f36..abd7698ed92 100644 --- a/src/vs/base/common/marshallingIds.ts +++ b/src/vs/base/common/marshallingIds.ts @@ -20,6 +20,5 @@ export const enum MarshalledId { NotebookCellActionContext, NotebookActionContext, TestItemContext, - Date, - Float32Array + Date } diff --git a/src/vs/base/parts/ipc/node/ipc.mp.ts b/src/vs/base/parts/ipc/node/ipc.mp.ts index 1a8b107dc99..b1648d260d6 100644 --- a/src/vs/base/parts/ipc/node/ipc.mp.ts +++ b/src/vs/base/parts/ipc/node/ipc.mp.ts @@ -43,7 +43,7 @@ export interface IClientConnectionFilter { * @returns `true` if the event was handled * and should not be processed by the server. */ - handled(e: MessageEvent): boolean; + handledClientConnection(e: MessageEvent): boolean; } /** @@ -58,7 +58,7 @@ export class Server extends IPCServer { const onCreateMessageChannel = new Emitter(); process.parentPort.on('message', (e: MessageEvent) => { - if (filter?.handled(e)) { + if (filter?.handledClientConnection(e)) { return; } diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts index cbd23de96c4..e195f2b8b7a 100644 --- a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts +++ b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts @@ -35,6 +35,7 @@ export class VoiceTranscriber extends Disposable { this._register(toDisposable(() => port.off('message', portHandler))); port.start(); + this._register(toDisposable(() => port.close())); })); } } diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 91e93fccc59..63b36ab8b0f 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -117,7 +117,7 @@ import { NativeEnvironmentService } from 'vs/platform/environment/node/environme import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; import { VoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; import { VoiceTranscriber } from 'vs/code/node/sharedProcess/contrib/voiceTranscriber'; -import { RawSharedProcessConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; +import { SharedProcessRawConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; class SharedProcessMain extends Disposable implements IClientConnectionFilter { @@ -439,14 +439,14 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { }); } - handled(e: MessageEvent): boolean { + handledClientConnection(e: MessageEvent): boolean { // This filter on message port messages will look for // attempts of a window to connect raw to the shared // process to handle these connections separate from // our IPC based protocol. - if (e.data !== RawSharedProcessConnection.response) { + if (e.data !== SharedProcessRawConnection.response) { return false; } diff --git a/src/vs/platform/sharedProcess/common/sharedProcess.ts b/src/vs/platform/sharedProcess/common/sharedProcess.ts index 7e443fffb12..5b7e60efc80 100644 --- a/src/vs/platform/sharedProcess/common/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/common/sharedProcess.ts @@ -9,12 +9,12 @@ export const SharedProcessLifecycle = { initDone: 'vscode:shared-process->electron-main=init-done' }; -export const ChannelSharedProcessConnection = { - request: 'vscode:createChannelSharedProcessConnection', - response: 'vscode:createChannelSharedProcessConnectionResult' +export const SharedProcessChannelConnection = { + request: 'vscode:createSharedProcessChannelConnection', + response: 'vscode:createSharedProcessChannelConnectionResult' }; -export const RawSharedProcessConnection = { - request: 'vscode:createRawSharedProcessConnection', - response: 'vscode:createRawSharedProcessConnectionResult' +export const SharedProcessRawConnection = { + request: 'vscode:createSharedProcessRawConnection', + response: 'vscode:createSharedProcessRawConnectionResult' }; diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index ec41ba8cc7a..c11fa7d0fc5 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -18,7 +18,7 @@ import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utility import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { parseSharedProcessDebugPort } from 'vs/platform/environment/node/environmentService'; import { assertIsDefined } from 'vs/base/common/types'; -import { ChannelSharedProcessConnection, RawSharedProcessConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; +import { SharedProcessChannelConnection, SharedProcessRawConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; export class SharedProcess extends Disposable { @@ -43,10 +43,10 @@ export class SharedProcess extends Disposable { private registerListeners(): void { // Shared process channel connections from workbench windows - validatedIpcMain.on(ChannelSharedProcessConnection.request, (e, nonce: string) => this.onWindowConnection(e, nonce, ChannelSharedProcessConnection.response)); + validatedIpcMain.on(SharedProcessChannelConnection.request, (e, nonce: string) => this.onWindowConnection(e, nonce, SharedProcessChannelConnection.response)); // Shared process raw connections from workbench windows - validatedIpcMain.on(RawSharedProcessConnection.request, (e, nonce: string) => this.onWindowConnection(e, nonce, RawSharedProcessConnection.response)); + validatedIpcMain.on(SharedProcessRawConnection.request, (e, nonce: string) => this.onWindowConnection(e, nonce, SharedProcessRawConnection.response)); // Lifecycle this._register(this.lifecycleMainService.onWillShutdown(() => this.onWillShutdown())); diff --git a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts index 11a2fd6a72b..fffa5df2feb 100644 --- a/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts +++ b/src/vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessService.ts @@ -8,7 +8,7 @@ import { IChannel, IServerChannel, getDelayedChannel } from 'vs/base/parts/ipc/c import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; -import { ChannelSharedProcessConnection, RawSharedProcessConnection } from 'vs/platform/sharedProcess/common/sharedProcess'; +import { SharedProcessChannelConnection, SharedProcessRawConnection } from 'vs/platform/sharedProcess/common/sharedProcess'; import { mark } from 'vs/base/common/performance'; import { Barrier, timeout } from 'vs/base/common/async'; import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; @@ -45,7 +45,7 @@ export class SharedProcessService extends Disposable implements ISharedProcessSe // Acquire a message port connected to the shared process mark('code/willConnectSharedProcess'); this.logService.trace('Renderer->SharedProcess#connect: before acquirePort'); - const port = await acquirePort(ChannelSharedProcessConnection.request, ChannelSharedProcessConnection.response); + const port = await acquirePort(SharedProcessChannelConnection.request, SharedProcessChannelConnection.response); mark('code/didConnectSharedProcess'); this.logService.trace('Renderer->SharedProcess#connect: connection established'); @@ -69,11 +69,11 @@ export class SharedProcessService extends Disposable implements ISharedProcessSe async createRawConnection(): Promise { // Await initialization of the shared process - await this.connect(); + await this.withSharedProcessConnection; // Create a new port to the shared process this.logService.trace('Renderer->SharedProcess#createRawConnection: before acquirePort'); - const port = await acquirePort(RawSharedProcessConnection.request, RawSharedProcessConnection.response); + const port = await acquirePort(SharedProcessRawConnection.request, SharedProcessRawConnection.response); this.logService.trace('Renderer->SharedProcess#createRawConnection: connection established'); return port; diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js index 3e4d6641c22..7b99644fa3c 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js @@ -13,7 +13,7 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { super(); this.channelCount = 1; - this.bufferTimespan = 4000; + this.bufferTimespan = 2000; this.startTime = undefined; this.allInputFloat32Array = undefined; @@ -27,16 +27,16 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { // @ts-ignore const port = this.port; port.onmessage = event => { - if (event.data === 'vscode:transferPortToAudioWorklet') { - this.sharedProcessPort = event.ports[0]; + if (event.data === 'vscode:transferSharedProcessConnection') { + this.sharedProcessConnection = event.ports[0]; - this.sharedProcessPort.onmessage = event => { + this.sharedProcessConnection.onmessage = event => { if (typeof event.data === 'string') { port.postMessage(event.data); } }; - this.sharedProcessPort.start(); + this.sharedProcessConnection.start(); } }; } @@ -56,14 +56,13 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { this.currentInputFloat32Arrays.push(inputChannelData.slice(0)); - if (Date.now() - this.startTime > this.bufferTimespan && this.sharedProcessPort) { + if (Date.now() - this.startTime > this.bufferTimespan && this.sharedProcessConnection) { const currentInputFloat32Arrays = this.currentInputFloat32Arrays; this.currentInputFloat32Arrays = []; this.allInputFloat32Array = this.joinFloat32Arrays(this.allInputFloat32Array ? [this.allInputFloat32Array, ...currentInputFloat32Arrays] : currentInputFloat32Arrays); - // @ts-ignore - this.sharedProcessPort.postMessage(this.allInputFloat32Array); + this.sharedProcessConnection.postMessage(this.allInputFloat32Array); this.startTime = Date.now(); } diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 523b07d1066..4ccbde62b10 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -50,10 +50,10 @@ class BufferedVoiceTranscriber extends AudioWorkletNode { } async start(token: CancellationToken): Promise { - const rawSharedProcessConnection = await this.sharedProcessService.createRawConnection(); - token.onCancellationRequested(() => rawSharedProcessConnection.close()); + const sharedProcessConnection = await this.sharedProcessService.createRawConnection(); + token.onCancellationRequested(() => sharedProcessConnection.close()); - this.port.postMessage('vscode:transferPortToAudioWorklet', [rawSharedProcessConnection]); + this.port.postMessage('vscode:transferSharedProcessConnection', [sharedProcessConnection]); } } From ace7ce5f52efb90ced1d94d263f84178834f1ff8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2023 12:32:33 +0200 Subject: [PATCH 023/607] voice - convert audio worklet to typescript --- ...scriber.js => bufferedVoiceTranscriber.ts} | 49 +++++++++---------- .../workbenchVoiceRecognitionService.ts | 1 - 2 files changed, 22 insertions(+), 28 deletions(-) rename src/vs/workbench/services/voiceRecognition/electron-sandbox/{bufferedVoiceTranscriber.js => bufferedVoiceTranscriber.ts} (68%) diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.ts similarity index 68% rename from src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js rename to src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.ts index 7b99644fa3c..bb6c8eaaa17 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.ts @@ -3,36 +3,38 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -//@ts-check -'use strict'; +declare class AudioWorkletProcessor { + + readonly port: MessagePort; + + process(inputs: [Float32Array[]], outputs: [Float32Array[]]): boolean; +} -// @ts-ignore class BufferedVoiceTranscriber extends AudioWorkletProcessor { + private static readonly BUFFER_TIMESPAN = 2000; + + private startTime: number | undefined = undefined; + + private allInputFloat32Array: Float32Array | undefined = undefined; + private currentInputFloat32Arrays: Float32Array[] = []; + + private sharedProcessConnection: MessagePort | undefined = undefined; + constructor() { super(); - this.channelCount = 1; - this.bufferTimespan = 2000; - this.startTime = undefined; - - this.allInputFloat32Array = undefined; - this.currentInputFloat32Arrays = []; // buffer over the duration of bufferTimespan - this.registerListeners(); } - registerListeners() { - - // @ts-ignore - const port = this.port; - port.onmessage = event => { + private registerListeners() { + this.port.onmessage = event => { if (event.data === 'vscode:transferSharedProcessConnection') { this.sharedProcessConnection = event.ports[0]; this.sharedProcessConnection.onmessage = event => { if (typeof event.data === 'string') { - port.postMessage(event.data); + this.port.postMessage(event.data); } }; @@ -41,22 +43,19 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { }; } - /** - * @param {[[Float32Array]]} inputs - */ - process(inputs) { + override process(inputs: [Float32Array[]]): boolean { if (this.startTime === undefined) { this.startTime = Date.now(); } const inputChannelData = inputs[0][0]; if ((!(inputChannelData instanceof Float32Array))) { - return; + return true; } this.currentInputFloat32Arrays.push(inputChannelData.slice(0)); - if (Date.now() - this.startTime > this.bufferTimespan && this.sharedProcessConnection) { + if (Date.now() - this.startTime > BufferedVoiceTranscriber.BUFFER_TIMESPAN && this.sharedProcessConnection) { const currentInputFloat32Arrays = this.currentInputFloat32Arrays; this.currentInputFloat32Arrays = []; @@ -70,11 +69,7 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { return true; } - /** - * @param {Float32Array[]} float32Arrays - * @returns {Float32Array} - */ - joinFloat32Arrays(float32Arrays) { + private joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array { const result = new Float32Array(float32Arrays.reduce((acc, curr) => acc + curr.length, 0)); let offset = 0; diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 4ccbde62b10..329b340651b 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -62,7 +62,6 @@ class BufferedVoiceTranscriber extends AudioWorkletNode { // - how to make this a singleton service that enables ref-counting on multiple callers? // - cancellation should flow to the shared process // - voice module should directly transcribe the PCM32 data without wav+file conversion -// - the audio worklet should be a TS file (try without any import/export?) export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { From 614bfb3d3352f7b848df98d68af95797063577fc Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 17 Aug 2023 14:07:03 +0200 Subject: [PATCH 024/607] sending diagnostics on file open --- .../src/languageFeatures/diagnostics.ts | 50 +++++++++---------- .../src/typescriptServiceClient.ts | 4 ++ 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 5981e63ea6e..515ee7889fb 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -154,7 +154,6 @@ export class DiagnosticsManager extends Disposable { private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; private readonly _pendingUpdates: ResourceMap; - private readonly _telemetryReporter: TelemetryReporter; private readonly _updateDelay = 50; @@ -168,7 +167,30 @@ export class DiagnosticsManager extends Disposable { this._pendingUpdates = new ResourceMap(undefined, { onCaseInsensitiveFileSystem }); this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); - this._telemetryReporter = telemetryReporter; + this._register(vscode.workspace.onDidOpenTextDocument((document) => { + const diagnostics = this.getDiagnostics(document.uri); + const diagnoticCodes = diagnostics.reduce(function (result: number[], d: vscode.Diagnostic) { + const code = d.code; + if (typeof code === 'string' || typeof code === 'number') { + result.push(Number(code)); + } else if (code !== undefined) { + result.push(Number(code.value)); + } + return result; + }, []).sort(); + /* __GDPR__ + "typescript.diagnostics" : { + "owner": "@aiday-mar", + "diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + telemetryReporter.logTelemetry('typescript.diagnostics', { + diagnoticCodes: diagnoticCodes.join(', ') + }); + })); } public override dispose() { @@ -242,29 +264,7 @@ export class DiagnosticsManager extends Disposable { } public getDiagnostics(file: vscode.Uri): ReadonlyArray { - const diagnostics = this._currentDiagnostics.get(file) || []; - const diagnoticCodes = diagnostics.reduce(function (result: number[], d: vscode.Diagnostic) { - const code = d.code; - if (typeof code === 'string' || typeof code === 'number') { - result.push(Number(code)); - } else if (code !== undefined) { - result.push(Number(code.value)); - } - return result; - }, []).sort(); - /* __GDPR__ - "typescript.diagnostics" : { - "owner": "@aiday-mar", - "diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this._telemetryReporter.logTelemetry('typescript.diagnostics', { - diagnoticCodes: diagnoticCodes.join(', ') - }); - return diagnostics; + return this._currentDiagnostics.get(file) || []; } private scheduleDiagnosticsUpdate(file: vscode.Uri) { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index c165f82db40..e9e303e5b4f 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -468,6 +468,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType } this.serviceExited(!this.isRestarting); this.isRestarting = false; + + // TODO: on handle exit, we need to send the typescript errors that we have accumulated + console.log('on handle exit'); }); handle.onEvent(event => this.dispatchEvent(event)); @@ -579,6 +582,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType }; } + // TODO: service exited will close the client service, is this the one that we need to monitor? private serviceExited(restart: boolean): void { this.loadingIndicator.reset(); From ce752f607aa2cd3912e2daac7d554fcb35e6752e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2023 14:10:01 +0200 Subject: [PATCH 025/607] voice - better ports lifecycle --- build/gulpfile.vscode.js | 2 +- .../sharedProcess/contrib/voiceTranscriber.ts | 16 ++++++- .../common/voiceRecognitionService.ts | 3 +- .../node/voiceRecognitionService.ts | 6 +-- ...criber.ts => voiceTranscriptionWorklet.ts} | 44 +++++++++++++------ .../workbenchVoiceRecognitionService.ts | 38 +++++++++++----- 6 files changed, 78 insertions(+), 31 deletions(-) rename src/vs/workbench/services/voiceRecognition/electron-sandbox/{bufferedVoiceTranscriber.ts => voiceTranscriptionWorklet.ts} (68%) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index cd0a762dfa7..5f7f5ce4470 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -72,7 +72,7 @@ const vscodeResources = [ 'out-build/vs/workbench/contrib/terminal/browser/media/*.sh', 'out-build/vs/workbench/contrib/terminal/browser/media/*.zsh', 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', - 'out-build/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js', + 'out-build/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', '!**/test/**' diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts index e195f2b8b7a..48a7843730a 100644 --- a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts +++ b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts @@ -7,12 +7,15 @@ import { Event } from 'vs/base/common/event'; import { MessagePortMain, MessageEvent } from 'vs/base/parts/sandbox/node/electronTypes'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; +import { ILogService } from 'vs/platform/log/common/log'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export class VoiceTranscriber extends Disposable { constructor( private readonly onDidWindowConnectRaw: Event, @IVoiceRecognitionService private readonly voiceRecognitionService: IVoiceRecognitionService, + @ILogService private readonly logService: ILogService ) { super(); @@ -21,12 +24,17 @@ export class VoiceTranscriber extends Disposable { private registerListeners(): void { this._register(this.onDidWindowConnectRaw(port => { + this.logService.info(`[voice] transcriber: new connection`); + + const cts = new CancellationTokenSource(); + this._register(toDisposable(() => cts.dispose(true))); + const portHandler = async (e: MessageEvent) => { if (!(e.data instanceof Float32Array)) { return; } - const result = await this.voiceRecognitionService.transcribe(e.data); + const result = await this.voiceRecognitionService.transcribe(e.data, cts.token); port.postMessage(result); }; @@ -36,6 +44,12 @@ export class VoiceTranscriber extends Disposable { port.start(); this._register(toDisposable(() => port.close())); + + port.on('close', () => { + this.logService.info(`[voice] transcriber: closed connection`); + + cts.dispose(true); + }); })); } } diff --git a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts index a563a2e45cd..ca52963092f 100644 --- a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); @@ -21,5 +22,5 @@ export interface IVoiceRecognitionService { * - 16khz sampling rate * - 16bit sample size */ - transcribe(channelData: Float32Array): Promise; + transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise; } diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index a67fb2c54b4..f1e259b76ad 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; @@ -14,7 +15,7 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { @ILogService private readonly logService: ILogService ) { } - async transcribe(channelData: Float32Array): Promise { + async transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise { this.logService.info(`[voice] transcribe(${channelData.length}): Begin`); const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; @@ -23,7 +24,6 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { } const now = Date.now(); - const conversionTime = Date.now() - now; const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; sampleRate: 16000; sampleSize: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); @@ -37,7 +37,7 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { suppressNonSpeechTokens: true }); - this.logService.info(`[voice] transcribe(${channelData.length}): End (text: "${text}", took: ${Date.now() - now}ms total, ${conversionTime}ms uint8->float32 conversion)`); + this.logService.info(`[voice] transcribe(${channelData.length}): End (text: "${text}", took: ${Date.now() - now}ms)`); return text; } diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts similarity index 68% rename from src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.ts rename to src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts index bb6c8eaaa17..d6edf688d1f 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts @@ -10,7 +10,7 @@ declare class AudioWorkletProcessor { process(inputs: [Float32Array[]], outputs: [Float32Array[]]): boolean; } -class BufferedVoiceTranscriber extends AudioWorkletProcessor { +class VoiceTranscriptionWorklet extends AudioWorkletProcessor { private static readonly BUFFER_TIMESPAN = 2000; @@ -21,6 +21,8 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { private sharedProcessConnection: MessagePort | undefined = undefined; + private stopped: boolean = false; + constructor() { super(); @@ -29,16 +31,32 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { private registerListeners() { this.port.onmessage = event => { - if (event.data === 'vscode:transferSharedProcessConnection') { - this.sharedProcessConnection = event.ports[0]; + switch (event.data) { + case 'vscode:startVoiceTranscription': { + this.sharedProcessConnection = event.ports[0]; - this.sharedProcessConnection.onmessage = event => { - if (typeof event.data === 'string') { - this.port.postMessage(event.data); - } - }; + this.sharedProcessConnection.onmessage = event => { + if (this.stopped) { + return; + } - this.sharedProcessConnection.start(); + if (typeof event.data === 'string') { + this.port.postMessage(event.data); + } + }; + + this.sharedProcessConnection.start(); + break; + } + + case 'vscode:stopVoiceTranscription': { + this.stopped = true; + + this.sharedProcessConnection?.close(); + this.sharedProcessConnection = undefined; + + break; + } } }; } @@ -50,12 +68,12 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { const inputChannelData = inputs[0][0]; if ((!(inputChannelData instanceof Float32Array))) { - return true; + return !this.stopped; } this.currentInputFloat32Arrays.push(inputChannelData.slice(0)); - if (Date.now() - this.startTime > BufferedVoiceTranscriber.BUFFER_TIMESPAN && this.sharedProcessConnection) { + if (Date.now() - this.startTime > VoiceTranscriptionWorklet.BUFFER_TIMESPAN && this.sharedProcessConnection) { const currentInputFloat32Arrays = this.currentInputFloat32Arrays; this.currentInputFloat32Arrays = []; @@ -66,7 +84,7 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { this.startTime = Date.now(); } - return true; + return !this.stopped; } private joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array { @@ -83,4 +101,4 @@ class BufferedVoiceTranscriber extends AudioWorkletProcessor { } // @ts-ignore -registerProcessor('buffered-voice-transcriber', BufferedVoiceTranscriber); +registerProcessor('voice-transcription-worklet', VoiceTranscriptionWorklet); diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 329b340651b..a676ed1aff5 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -28,7 +28,7 @@ export interface IWorkbenchVoiceRecognitionService { transcribe(cancellation: CancellationToken): Event; } -class BufferedVoiceTranscriber extends AudioWorkletNode { +class VoiceTranscriptionWorkletNode extends AudioWorkletNode { constructor( context: BaseAudioContext, @@ -36,7 +36,7 @@ class BufferedVoiceTranscriber extends AudioWorkletNode { private readonly onDidTranscribe: Emitter, private readonly sharedProcessService: ISharedProcessService ) { - super(context, 'buffered-voice-transcriber', options); + super(context, 'voice-transcription-worklet', options); this.registerListeners(); } @@ -51,16 +51,19 @@ class BufferedVoiceTranscriber extends AudioWorkletNode { async start(token: CancellationToken): Promise { const sharedProcessConnection = await this.sharedProcessService.createRawConnection(); - token.onCancellationRequested(() => sharedProcessConnection.close()); - this.port.postMessage('vscode:transferSharedProcessConnection', [sharedProcessConnection]); + token.onCancellationRequested(() => { + this.port.postMessage('vscode:stopVoiceTranscription'); + this.disconnect(); + }); + + this.port.postMessage('vscode:startVoiceTranscription', [sharedProcessConnection]); } } // TODO@voice // - how to prevent data processing accumulation when processing is slow? // - how to make this a singleton service that enables ref-counting on multiple callers? -// - cancellation should flow to the shared process // - voice module should directly transcribe the PCM32 data without wav+file conversion export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { @@ -85,8 +88,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit return onDidTranscribe.event; } - private async doTranscribe(onDidTranscribe: Emitter, token: CancellationToken): Promise { - return this.progressService.withProgress({ + private doTranscribe(onDidTranscribe: Emitter, token: CancellationToken): void { + this.progressService.withProgress({ location: ProgressLocation.Window, title: localize('voiceTranscription', "Voice Transcription"), }, async progress => { @@ -116,21 +119,32 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit const microphoneSource = audioContext.createMediaStreamSource(microphoneDevice); token.onCancellationRequested(() => { - microphoneDevice.getTracks().forEach(track => track.stop()); + for (const track of microphoneDevice.getTracks()) { + track.stop(); + } + microphoneSource.disconnect(); audioContext.close(); recordingDone.complete(); }); - await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/bufferedVoiceTranscriber.js').toString(true)); + await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js').toString(true)); - const bufferedVoiceTranscriberTarget = new BufferedVoiceTranscriber(audioContext, { + if (token.isCancellationRequested) { + return; + } + + const voiceTranscriptionTarget = new VoiceTranscriptionWorkletNode(audioContext, { channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, channelCountMode: 'explicit' }, onDidTranscribe, this.sharedProcessService); - await bufferedVoiceTranscriberTarget.start(token); + await voiceTranscriptionTarget.start(token); - microphoneSource.connect(bufferedVoiceTranscriberTarget); + if (token.isCancellationRequested) { + return; + } + + microphoneSource.connect(voiceTranscriptionTarget); progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); From 0d4db5ffc407ffab5058dc5ea7de1dc511a5d150 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 17 Aug 2023 14:35:27 +0200 Subject: [PATCH 026/607] sending the diagnostics on handle exit --- .../src/languageFeatures/diagnostics.ts | 50 +++++++++++++------ .../src/typescriptServiceClient.ts | 6 +-- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 515ee7889fb..8a4a0c79e95 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -149,12 +149,40 @@ class DiagnosticSettings { } } +class DiagnosticsTelemetryManager { + + private _diagnosticCodes: number[] = []; + + constructor(private readonly _telemetryReporter: TelemetryReporter) { } + + public addDiagnosticCodes(codes: number[]) { + this._diagnosticCodes.push(...codes); + this._diagnosticCodes.sort(); + } + + public sendDiagnosticsCodesTelemetry(): void { + /* __GDPR__ + "typescript.diagnostics" : { + "owner": "@aiday-mar", + "diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this._telemetryReporter.logTelemetry('typescript.diagnostics', { + diagnoticCodes: this._diagnosticCodes.join(', ') + }); + this._diagnosticCodes = []; + } +} + export class DiagnosticsManager extends Disposable { private readonly _diagnostics: ResourceMap; private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; private readonly _pendingUpdates: ResourceMap; - + private readonly _diagnosticsTelemetryManager: DiagnosticsTelemetryManager; private readonly _updateDelay = 50; constructor( @@ -167,6 +195,7 @@ export class DiagnosticsManager extends Disposable { this._pendingUpdates = new ResourceMap(undefined, { onCaseInsensitiveFileSystem }); this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); + this._diagnosticsTelemetryManager = new DiagnosticsTelemetryManager(telemetryReporter); this._register(vscode.workspace.onDidOpenTextDocument((document) => { const diagnostics = this.getDiagnostics(document.uri); const diagnoticCodes = diagnostics.reduce(function (result: number[], d: vscode.Diagnostic) { @@ -177,22 +206,15 @@ export class DiagnosticsManager extends Disposable { result.push(Number(code.value)); } return result; - }, []).sort(); - /* __GDPR__ - "typescript.diagnostics" : { - "owner": "@aiday-mar", - "diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - telemetryReporter.logTelemetry('typescript.diagnostics', { - diagnoticCodes: diagnoticCodes.join(', ') - }); + }, []); + this._diagnosticsTelemetryManager.addDiagnosticCodes(diagnoticCodes); })); } + public sendDiagnosticsCodesTelemetry(): void { + this._diagnosticsTelemetryManager.sendDiagnosticsCodesTelemetry(); + } + public override dispose() { super.dispose(); diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index e9e303e5b4f..d04f41e4142 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -456,7 +456,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } */ this.logTelemetry('tsserver.exitWithCode', { code: code ?? undefined, signal: signal ?? undefined }); - + this.diagnosticsManager.sendDiagnosticsCodesTelemetry(); if (this.token !== mytoken) { // this is coming from an old process @@ -468,9 +468,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType } this.serviceExited(!this.isRestarting); this.isRestarting = false; - - // TODO: on handle exit, we need to send the typescript errors that we have accumulated - console.log('on handle exit'); }); handle.onEvent(event => this.dispatchEvent(event)); @@ -582,7 +579,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType }; } - // TODO: service exited will close the client service, is this the one that we need to monitor? private serviceExited(restart: boolean): void { this.loadingIndicator.reset(); From 733530d9e5842337c011915e1259d5c1b1f6387a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2023 14:36:19 +0200 Subject: [PATCH 027/607] voice - implement sequential transcriptions --- src/vs/base/common/async.ts | 6 +- .../sharedProcess/contrib/voiceTranscriber.ts | 98 +++++++++++++------ .../node/sharedProcess/sharedProcessMain.ts | 4 +- 3 files changed, 73 insertions(+), 35 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index e00f3d59653..6b87b06b21a 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -1294,12 +1294,8 @@ export class TaskSequentializer { private _next?: INextTask; hasPending(taskId?: number): this is ITaskSequentializerWithPendingTask { - if (!this._pending) { - return false; - } - if (typeof taskId === 'number') { - return this._pending.taskId === taskId; + return this._pending?.taskId === taskId; } return !!this._pending; diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts index 48a7843730a..6b8f8bf3084 100644 --- a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts +++ b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts @@ -8,9 +8,10 @@ import { MessagePortMain, MessageEvent } from 'vs/base/parts/sandbox/node/electr import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; import { ILogService } from 'vs/platform/log/common/log'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { TaskSequentializer } from 'vs/base/common/async'; -export class VoiceTranscriber extends Disposable { +export class VoiceTranscriptionManager extends Disposable { constructor( private readonly onDidWindowConnectRaw: Event, @@ -24,32 +25,73 @@ export class VoiceTranscriber extends Disposable { private registerListeners(): void { this._register(this.onDidWindowConnectRaw(port => { - this.logService.info(`[voice] transcriber: new connection`); - - const cts = new CancellationTokenSource(); - this._register(toDisposable(() => cts.dispose(true))); - - const portHandler = async (e: MessageEvent) => { - if (!(e.data instanceof Float32Array)) { - return; - } - - const result = await this.voiceRecognitionService.transcribe(e.data, cts.token); - - port.postMessage(result); - }; - - port.on('message', portHandler); - this._register(toDisposable(() => port.off('message', portHandler))); - - port.start(); - this._register(toDisposable(() => port.close())); - - port.on('close', () => { - this.logService.info(`[voice] transcriber: closed connection`); - - cts.dispose(true); - }); + this._register(new VoiceTranscriber(port, this.voiceRecognitionService, this.logService)); })); } } + +class VoiceTranscriber extends Disposable { + + private readonly transcriptionSequentializer = new TaskSequentializer(); + + private requests = 0; + + constructor( + private readonly port: MessagePortMain, + private readonly voiceRecognitionService: IVoiceRecognitionService, + private readonly logService: ILogService + ) { + super(); + + this.registerListeners(); + } + + private registerListeners(): void { + this.logService.info(`[voice] transcriber: new connection`); + + const cts = new CancellationTokenSource(); + this._register(toDisposable(() => cts.dispose(true))); + + const requestHandler = (e: MessageEvent) => this.handleRequest(e, cts.token); + this.port.on('message', requestHandler); + this._register(toDisposable(() => this.port.off('message', requestHandler))); + + this.port.start(); + this._register(toDisposable(() => this.port.close())); + + this.port.on('close', () => { + this.logService.info(`[voice] transcriber: closed connection`); + + cts.dispose(true); + this.transcriptionSequentializer.cancelPending(); + }); + } + + private async handleRequest(e: MessageEvent, cancellation: CancellationToken): Promise { + if (!(e.data instanceof Float32Array)) { + return; + } + + this.requests++; + + if (!this.transcriptionSequentializer.hasPending()) { + this.transcriptionSequentializer.setPending(this.requests, this.transcribe(e.data, cancellation)); + } else { + this.transcriptionSequentializer.setNext(() => this.transcribe(e.data, cancellation)); + } + } + + private async transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise { + if (cancellation.isCancellationRequested) { + return; + } + + const result = await this.voiceRecognitionService.transcribe(channelData, cancellation); + + if (cancellation.isCancellationRequested) { + return; + } + + this.port.postMessage(result); + } +} diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index 63b36ab8b0f..bf30c403b93 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -116,7 +116,7 @@ import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; import { VoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; -import { VoiceTranscriber } from 'vs/code/node/sharedProcess/contrib/voiceTranscriber'; +import { VoiceTranscriptionManager } from 'vs/code/node/sharedProcess/contrib/voiceTranscriber'; import { SharedProcessRawConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; class SharedProcessMain extends Disposable implements IClientConnectionFilter { @@ -179,7 +179,7 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { instantiationService.createInstance(LocalizationsUpdater), instantiationService.createInstance(ExtensionsContributions), instantiationService.createInstance(UserDataProfilesCleaner), - instantiationService.createInstance(VoiceTranscriber, this.onDidWindowConnectRaw.event) + instantiationService.createInstance(VoiceTranscriptionManager, this.onDidWindowConnectRaw.event) )); } From bf1f6a2298fdfdd09060336417a5e892176200c7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 17 Aug 2023 14:38:48 +0200 Subject: [PATCH 028/607] voice - update todos --- .../electron-sandbox/workbenchVoiceRecognitionService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index a676ed1aff5..7415d4dc6b5 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -62,8 +62,6 @@ class VoiceTranscriptionWorkletNode extends AudioWorkletNode { } // TODO@voice -// - how to prevent data processing accumulation when processing is slow? -// - how to make this a singleton service that enables ref-counting on multiple callers? // - voice module should directly transcribe the PCM32 data without wav+file conversion export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { From 91877b8abf8f5b35d771d6f6e07b0a84ed8a3fc7 Mon Sep 17 00:00:00 2001 From: kon72 Date: Tue, 15 Aug 2023 06:35:23 +0900 Subject: [PATCH 029/607] Add commands for collapsing/showing all unchanged regions --- .../diffEditorWidget2/diffEditorViewModel.ts | 7 ++- .../diffEditorWidget2.contribution.ts | 45 +++++++++++++++++++ .../diffEditorWidget2/diffEditorWidget2.ts | 16 +++++++ .../diffEditorWidget2/unchangedRanges.ts | 4 +- 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 4833d3de651..728bdc1976f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -39,7 +39,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo // Reset state transaction(tx => { for (const r of this._unchangedRegions.get().regions) { - r.setState(0, 0, tx); + r.collapseAll(tx); } }); return []; @@ -401,6 +401,11 @@ export class UnchangedRegion { this._visibleLineCountBottom.set(this.lineCount - this._visibleLineCountTop.get(), tx); } + public collapseAll(tx: ITransaction | undefined): void { + this._visibleLineCountTop.set(0, tx); + this._visibleLineCountBottom.set(0, tx); + } + public setState(visibleLineCountTop: number, visibleLineCountBottom: number, tx: ITransaction | undefined): void { visibleLineCountTop = Math.max(Math.min(visibleLineCountTop, this.lineCount), 0); visibleLineCountBottom = Math.max(Math.min(visibleLineCountBottom, this.lineCount - visibleLineCountTop), 0); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts index d312eb5629a..ec2d305bda9 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts @@ -112,6 +112,7 @@ const diffEditorCategory: ILocalizedString = { value: localize('diffEditor', 'Diff Editor'), original: 'Diff Editor', }; + export class SwitchSide extends EditorAction2 { constructor() { super({ @@ -133,3 +134,47 @@ export class SwitchSide extends EditorAction2 { } registerAction2(SwitchSide); + +export class CollapseAllUnchangedRegions extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.collapseAllUnchangedRegions', + title: { value: localize('collapseAllUnchangedRegions', "Collapse All Unchanged Regions"), original: 'Collapse All Unchanged Regions' }, + icon: Codicon.fold, + precondition: ContextKeyExpr.and(ContextKeyEqualsExpr.create('diffEditorVersion', 2), ContextKeyExpr.has('isInDiffEditor')), + f1: true, + category: diffEditorCategory, + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget2) { + diffEditor.collapseAllUnchangedRegions(); + } + } +} + +registerAction2(CollapseAllUnchangedRegions); + +export class ShowAllUnchangedRegions extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.showAllUnchangedRegions', + title: { value: localize('showAllUnchangedRegions', "Show All Unchanged Regions"), original: 'Show All Unchanged Regions' }, + icon: Codicon.unfold, + precondition: ContextKeyExpr.and(ContextKeyEqualsExpr.create('diffEditorVersion', 2), ContextKeyExpr.has('isInDiffEditor')), + f1: true, + category: diffEditorCategory, + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget2) { + diffEditor.showAllUnchangedRegions(); + } + } +} + +registerAction2(ShowAllUnchangedRegions); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 21b5f67aaae..13f039811af 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -512,6 +512,22 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { } destination.focus(); } + + collapseAllUnchangedRegions(): void { + const unchangedRegions = this._diffModel.get()?.unchangedRegions.get(); + if (!unchangedRegions) { return; } + for (const region of unchangedRegions) { + region.collapseAll(undefined); + } + } + + showAllUnchangedRegions(): void { + const unchangedRegions = this._diffModel.get()?.unchangedRegions.get(); + if (!unchangedRegions) { return; } + for (const region of unchangedRegions) { + region.showAll(undefined); + } + } } function translatePosition(posInOriginal: Position, mappings: LineRangeMapping[]): Range { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 7272f56b11f..8e953782b21 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -156,7 +156,7 @@ export class UnchangedRangesFeature extends Disposable { if (!model) { return; } const region = model.unchangedRegions.get().find(r => r.modifiedRange.includes(lineNumber)); if (!region) { return; } - region.setState(0, 0, undefined); + region.collapseAll(undefined); event.event.stopPropagation(); event.event.preventDefault(); } @@ -169,7 +169,7 @@ export class UnchangedRangesFeature extends Disposable { if (!model) { return; } const region = model.unchangedRegions.get().find(r => r.originalRange.includes(lineNumber)); if (!region) { return; } - region.setState(0, 0, undefined); + region.collapseAll(undefined); event.event.stopPropagation(); event.event.preventDefault(); } From 121d7b169dc158c1512030d8a1827ed89c25783d Mon Sep 17 00:00:00 2001 From: kon72 Date: Tue, 15 Aug 2023 22:56:00 +0900 Subject: [PATCH 030/607] Address review comments --- .../diffEditorWidget2/diffEditorWidget2.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 13f039811af..49b56595b03 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -516,17 +516,21 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { collapseAllUnchangedRegions(): void { const unchangedRegions = this._diffModel.get()?.unchangedRegions.get(); if (!unchangedRegions) { return; } - for (const region of unchangedRegions) { - region.collapseAll(undefined); - } + transaction(tx => { + for (const region of unchangedRegions) { + region.collapseAll(tx); + } + }); } showAllUnchangedRegions(): void { const unchangedRegions = this._diffModel.get()?.unchangedRegions.get(); if (!unchangedRegions) { return; } - for (const region of unchangedRegions) { - region.showAll(undefined); - } + transaction(tx => { + for (const region of unchangedRegions) { + region.showAll(tx); + } + }); } } From 81ae16ba6efea382fdb068c8859fcd116c767550 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 10:47:35 +0200 Subject: [PATCH 031/607] sending the telemetry event in a loop --- .../src/languageFeatures/diagnostics.ts | 102 +++++++++++------- .../src/typescriptServiceClient.ts | 2 +- 2 files changed, 63 insertions(+), 41 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 8a4a0c79e95..73495fb3301 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -9,6 +9,7 @@ import * as arrays from '../utils/arrays'; import { Disposable } from '../utils/dispose'; import { ResourceMap } from '../utils/resourceMap'; import { TelemetryReporter } from '../logging/telemetry'; +import { URI } from 'vscode-uri'; function diagnosticsEquals(a: vscode.Diagnostic, b: vscode.Diagnostic): boolean { if (a === b) { @@ -149,31 +150,68 @@ class DiagnosticSettings { } } -class DiagnosticsTelemetryManager { +/* +const diagnostics = this.getDiagnostics(document.uri); + const diagnoticCodes = diagnostics.reduce(function (result: number[], d: vscode.Diagnostic) { + const code = d.code; + if (typeof code === 'string' || typeof code === 'number') { + result.push(Number(code)); + } else if (code !== undefined) { + result.push(Number(code.value)); + } + return result; + }, []); + */ +class DiagnosticsTelemetryManager extends Disposable { - private _diagnosticCodes: number[] = []; + private readonly _timeOutDiagnosticMaps = new Map(); + private readonly _diagnosticCodesMap = new Map(); - constructor(private readonly _telemetryReporter: TelemetryReporter) { } - - public addDiagnosticCodes(codes: number[]) { - this._diagnosticCodes.push(...codes); - this._diagnosticCodes.sort(); + constructor(private readonly _telemetryReporter: TelemetryReporter) { + super(); + this._register(vscode.workspace.onDidChangeTextDocument(e => { + if (e.document.languageId === 'typescript') { + clearTimeout(this._timeOutDiagnosticMaps.get(e.document.uri)); + const timeOut = setTimeout(() => { }, 5000); + this._timeOutDiagnosticMaps.set(e.document.uri, timeOut); + } + })); + this._register(vscode.workspace.onDidOpenTextDocument(e => { + if (e.languageId === 'typescript') { + this._timeOutDiagnosticMaps.set(e.uri, undefined); + } + })); + this._register(vscode.workspace.onDidCloseTextDocument(e => { + if (e.languageId === 'typescript') { + this._timeOutDiagnosticMaps.delete(e.uri); + } + })); + this._sendTelemetryEvent(); } - public sendDiagnosticsCodesTelemetry(): void { - /* __GDPR__ - "typescript.diagnostics" : { - "owner": "@aiday-mar", - "diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "${include}": [ - "${TypeScriptCommonProperties}" - ] - } - */ - this._telemetryReporter.logTelemetry('typescript.diagnostics', { - diagnoticCodes: this._diagnosticCodes.join(', ') - }); - this._diagnosticCodes = []; + private _sendTelemetryEvent() { + setTimeout(() => { + if (this._diagnosticCodesMap.size > 0) { + let diagnosticCodes = ''; + this._diagnosticCodesMap.forEach((value, key) => { + diagnosticCodes += `${key}:${value},`; + }); + this._diagnosticCodesMap.clear(); + /* __GDPR__ + "typescript.diagnostics" : { + "owner": "@aiday-mar", + "diagnosticCodes" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this._telemetryReporter.logTelemetry('typescript.diagnostics', { + diagnoticCodes: diagnosticCodes + }); + } + this._sendTelemetryEvent(); + }, 5 * 60 * 1000); } } @@ -182,7 +220,6 @@ export class DiagnosticsManager extends Disposable { private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; private readonly _pendingUpdates: ResourceMap; - private readonly _diagnosticsTelemetryManager: DiagnosticsTelemetryManager; private readonly _updateDelay = 50; constructor( @@ -195,24 +232,9 @@ export class DiagnosticsManager extends Disposable { this._pendingUpdates = new ResourceMap(undefined, { onCaseInsensitiveFileSystem }); this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); - this._diagnosticsTelemetryManager = new DiagnosticsTelemetryManager(telemetryReporter); - this._register(vscode.workspace.onDidOpenTextDocument((document) => { - const diagnostics = this.getDiagnostics(document.uri); - const diagnoticCodes = diagnostics.reduce(function (result: number[], d: vscode.Diagnostic) { - const code = d.code; - if (typeof code === 'string' || typeof code === 'number') { - result.push(Number(code)); - } else if (code !== undefined) { - result.push(Number(code.value)); - } - return result; - }, []); - this._diagnosticsTelemetryManager.addDiagnosticCodes(diagnoticCodes); - })); - } - - public sendDiagnosticsCodesTelemetry(): void { - this._diagnosticsTelemetryManager.sendDiagnosticsCodesTelemetry(); + if (Math.random() * 1000 <= 1) { + this._register(new DiagnosticsTelemetryManager(telemetryReporter)); + } } public override dispose() { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index d04f41e4142..c165f82db40 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -456,7 +456,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } */ this.logTelemetry('tsserver.exitWithCode', { code: code ?? undefined, signal: signal ?? undefined }); - this.diagnosticsManager.sendDiagnosticsCodesTelemetry(); + if (this.token !== mytoken) { // this is coming from an old process From 008b00d24f3c3d8d89f70513bc9e1d5b164045fc Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 11:33:03 +0200 Subject: [PATCH 032/607] taking the diff between current and previous snapshot --- .../src/languageFeatures/diagnostics.ts | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 73495fb3301..4331278c4ff 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -10,6 +10,7 @@ import { Disposable } from '../utils/dispose'; import { ResourceMap } from '../utils/resourceMap'; import { TelemetryReporter } from '../logging/telemetry'; import { URI } from 'vscode-uri'; +import { diff } from 'semver'; function diagnosticsEquals(a: vscode.Diagnostic, b: vscode.Diagnostic): boolean { if (a === b) { @@ -165,14 +166,18 @@ const diagnostics = this.getDiagnostics(document.uri); class DiagnosticsTelemetryManager extends Disposable { private readonly _timeOutDiagnosticMaps = new Map(); + private readonly _diagnosticsSnapshot = new Map(); private readonly _diagnosticCodesMap = new Map(); - constructor(private readonly _telemetryReporter: TelemetryReporter) { + constructor( + private readonly _telemetryReporter: TelemetryReporter, + private readonly _getDiagnostics: (uri: URI) => readonly vscode.Diagnostic[] + ) { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { if (e.document.languageId === 'typescript') { clearTimeout(this._timeOutDiagnosticMaps.get(e.document.uri)); - const timeOut = setTimeout(() => { }, 5000); + const timeOut = setTimeout(() => { this._updateDiagnosticCodes(e.document.uri); }, 5000); this._timeOutDiagnosticMaps.set(e.document.uri, timeOut); } })); @@ -189,6 +194,21 @@ class DiagnosticsTelemetryManager extends Disposable { this._sendTelemetryEvent(); } + private _updateDiagnosticCodes(uri: URI) { + const previousDiagnostics = this._diagnosticsSnapshot.get(uri); + const currentDiagnostics = this._getDiagnostics(uri); + this._diagnosticsSnapshot.set(uri, currentDiagnostics); + const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => JSON.stringify(diagnostic) === JSON.stringify(previousDiagnostic))); + diagnosticsDiff.forEach((diagnostic) => { + const code = diagnostic.code; + if (typeof code === 'string' || typeof code === 'number') { + this._diagnosticCodesMap.set(Number(code), (this._diagnosticCodesMap.get(Number(code)) || 0) + 1); + } else if (code !== undefined) { + this._diagnosticCodesMap.set(Number(code.value), (this._diagnosticCodesMap.get(Number(code.value)) || 0) + 1); + } + }); + } + private _sendTelemetryEvent() { setTimeout(() => { if (this._diagnosticCodesMap.size > 0) { @@ -220,6 +240,7 @@ export class DiagnosticsManager extends Disposable { private readonly _settings = new DiagnosticSettings(); private readonly _currentDiagnostics: vscode.DiagnosticCollection; private readonly _pendingUpdates: ResourceMap; + private readonly _updateDelay = 50; constructor( @@ -233,7 +254,7 @@ export class DiagnosticsManager extends Disposable { this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); if (Math.random() * 1000 <= 1) { - this._register(new DiagnosticsTelemetryManager(telemetryReporter)); + this._register(new DiagnosticsTelemetryManager(telemetryReporter, this.getDiagnostics)); } } From 088f7cf83241acc0d67657b2885b55f9c76c5d4f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 18 Aug 2023 11:50:37 +0200 Subject: [PATCH 033/607] voice - only send new data to voice transcriber --- .../sharedProcess/contrib/voiceTranscriber.ts | 32 ++++++++++++++++--- .../node/sharedProcess/sharedProcessMain.ts | 3 +- .../common/voiceRecognitionService.ts | 26 --------------- .../node/voiceRecognitionService.ts | 21 +++++++++++- .../voiceTranscriptionWorklet.ts | 28 ++++------------ .../workbenchVoiceRecognitionService.ts | 3 +- 6 files changed, 56 insertions(+), 57 deletions(-) delete mode 100644 src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts index 6b8f8bf3084..d9a9979dcb3 100644 --- a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts +++ b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { MessagePortMain, MessageEvent } from 'vs/base/parts/sandbox/node/electronTypes'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; +import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { TaskSequentializer } from 'vs/base/common/async'; @@ -36,6 +36,8 @@ class VoiceTranscriber extends Disposable { private requests = 0; + private data: Float32Array | undefined = undefined; + constructor( private readonly port: MessagePortMain, private readonly voiceRecognitionService: IVoiceRecognitionService, @@ -68,16 +70,26 @@ class VoiceTranscriber extends Disposable { } private async handleRequest(e: MessageEvent, cancellation: CancellationToken): Promise { - if (!(e.data instanceof Float32Array)) { + if (!(Array.isArray(e.data))) { return; } + const newData: Float32Array[] = []; + for (const channelData of e.data) { + if (channelData instanceof Float32Array) { + newData.push(channelData); + } + } + + this.data = this.joinFloat32Arrays(this.data ? [this.data, ...newData] : newData); + const data = this.data.slice(0); + this.requests++; if (!this.transcriptionSequentializer.hasPending()) { - this.transcriptionSequentializer.setPending(this.requests, this.transcribe(e.data, cancellation)); + this.transcriptionSequentializer.setPending(this.requests, this.transcribe(data, cancellation)); } else { - this.transcriptionSequentializer.setNext(() => this.transcribe(e.data, cancellation)); + this.transcriptionSequentializer.setNext(() => this.transcribe(data, cancellation)); } } @@ -94,4 +106,16 @@ class VoiceTranscriber extends Disposable { this.port.postMessage(result); } + + private joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array { + const result = new Float32Array(float32Arrays.reduce((prev, curr) => prev + curr.length, 0)); + + let offset = 0; + for (const float32Array of float32Arrays) { + result.set(float32Array, offset); + offset += float32Array.length; + } + + return result; + } } diff --git a/src/vs/code/node/sharedProcess/sharedProcessMain.ts b/src/vs/code/node/sharedProcess/sharedProcessMain.ts index bf30c403b93..a81bedb9ab4 100644 --- a/src/vs/code/node/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/node/sharedProcess/sharedProcessMain.ts @@ -114,8 +114,7 @@ import { IRemoteSocketFactoryService, RemoteSocketFactoryService } from 'vs/plat import { RemoteConnectionType } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; -import { VoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; +import { IVoiceRecognitionService, VoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; import { VoiceTranscriptionManager } from 'vs/code/node/sharedProcess/contrib/voiceTranscriber'; import { SharedProcessRawConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; diff --git a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts deleted file mode 100644 index ca52963092f..00000000000 --- a/src/vs/platform/voiceRecognition/common/voiceRecognitionService.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CancellationToken } from 'vs/base/common/cancellation'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); - -export interface IVoiceRecognitionService { - - readonly _serviceBrand: undefined; - - /** - * Given a buffer of audio data, attempts to - * transcribe the spoken words into text. - * - * @param channelData the raw audio data obtained - * from the microphone as uncompressed PCM data: - * - 1 channel (mono) - * - 16khz sampling rate - * - 16bit sample size - */ - transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise; -} diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index f1e259b76ad..ca569eae27e 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -5,7 +5,26 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; -import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/common/voiceRecognitionService'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); + +export interface IVoiceRecognitionService { + + readonly _serviceBrand: undefined; + + /** + * Given a buffer of audio data, attempts to + * transcribe the spoken words into text. + * + * @param channelData the raw audio data obtained + * from the microphone as uncompressed PCM data: + * - 1 channel (mono) + * - 16khz sampling rate + * - 16bit sample size + */ + transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise; +} export class VoiceRecognitionService implements IVoiceRecognitionService { diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts index d6edf688d1f..a298b23b29d 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts @@ -15,14 +15,12 @@ class VoiceTranscriptionWorklet extends AudioWorkletProcessor { private static readonly BUFFER_TIMESPAN = 2000; private startTime: number | undefined = undefined; + private stopped: boolean = false; - private allInputFloat32Array: Float32Array | undefined = undefined; - private currentInputFloat32Arrays: Float32Array[] = []; + private buffer: Float32Array[] = []; private sharedProcessConnection: MessagePort | undefined = undefined; - private stopped: boolean = false; - constructor() { super(); @@ -71,33 +69,19 @@ class VoiceTranscriptionWorklet extends AudioWorkletProcessor { return !this.stopped; } - this.currentInputFloat32Arrays.push(inputChannelData.slice(0)); + this.buffer.push(inputChannelData.slice(0)); if (Date.now() - this.startTime > VoiceTranscriptionWorklet.BUFFER_TIMESPAN && this.sharedProcessConnection) { - const currentInputFloat32Arrays = this.currentInputFloat32Arrays; - this.currentInputFloat32Arrays = []; + const buffer = this.buffer; + this.buffer = []; - this.allInputFloat32Array = this.joinFloat32Arrays(this.allInputFloat32Array ? [this.allInputFloat32Array, ...currentInputFloat32Arrays] : currentInputFloat32Arrays); - - this.sharedProcessConnection.postMessage(this.allInputFloat32Array); + this.sharedProcessConnection.postMessage(buffer); this.startTime = Date.now(); } return !this.stopped; } - - private joinFloat32Arrays(float32Arrays: Float32Array[]): Float32Array { - const result = new Float32Array(float32Arrays.reduce((acc, curr) => acc + curr.length, 0)); - - let offset = 0; - for (const float32Array of float32Arrays) { - result.set(float32Array, offset); - offset += float32Array.length; - } - - return result; - } } // @ts-ignore diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 7415d4dc6b5..17260848ccf 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -62,8 +62,7 @@ class VoiceTranscriptionWorkletNode extends AudioWorkletNode { } // TODO@voice -// - voice module should directly transcribe the PCM32 data without wav+file conversion - +// - pass cancellation down into the node module via AbortSignal export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { declare readonly _serviceBrand: undefined; From c71094a794e4ffbc7592269ef0960f4a187dea8a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 18 Aug 2023 12:03:18 +0200 Subject: [PATCH 034/607] voice - stop processing after 30s --- .../node/sharedProcess/contrib/voiceTranscriber.ts | 11 ++++++++++- .../voiceRecognition/node/voiceRecognitionService.ts | 6 +++--- .../workbenchVoiceRecognitionService.ts | 10 +++++----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts index d9a9979dcb3..210570ef952 100644 --- a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts +++ b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts @@ -32,6 +32,8 @@ export class VoiceTranscriptionManager extends Disposable { class VoiceTranscriber extends Disposable { + private static MAX_DATA_LENGTH = 30 /* seconds */ * 16000 /* sampling rate */ * 16 /* bith depth */ * 1 /* channels */ / 8; + private readonly transcriptionSequentializer = new TaskSequentializer(); private requests = 0; @@ -81,7 +83,14 @@ class VoiceTranscriber extends Disposable { } } - this.data = this.joinFloat32Arrays(this.data ? [this.data, ...newData] : newData); + const dataCandidate = this.joinFloat32Arrays(this.data ? [this.data, ...newData] : newData); + + if (dataCandidate.length > VoiceTranscriber.MAX_DATA_LENGTH) { + this.logService.warn(`[voice] transcriber: refusing to accept more than 30s of audio data`); + return; + } + + this.data = dataCandidate; const data = this.data.slice(0); this.requests++; diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index ca569eae27e..a2a1d3218e9 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -44,11 +44,11 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { const now = Date.now(); - const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; sampleRate: 16000; sampleSize: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); + const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; samplingRate: 16000; bitDepth: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); const text = await voiceModule.transcribe({ - sampleRate: 16000, - sampleSize: 16, + samplingRate: 16000, + bitDepth: 16, channelCount: 1, channelData }, { diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 17260848ccf..e2e94971c3a 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -67,8 +67,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit declare readonly _serviceBrand: undefined; - private static readonly AUDIO_SAMPLE_RATE = 16000; - private static readonly AUDIO_SAMPLE_SIZE = 16; + private static readonly AUDIO_SAMPLING_RATE = 16000; + private static readonly AUDIO_BIT_DEPTH = 16; private static readonly AUDIO_CHANNELS = 1; constructor( @@ -96,8 +96,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit const microphoneDevice = await navigator.mediaDevices.getUserMedia({ audio: { - sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, - sampleSize: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_SIZE, + sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, + sampleSize: WorkbenchVoiceRecognitionService.AUDIO_BIT_DEPTH, channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, autoGainControl: true, noiseSuppression: true @@ -109,7 +109,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit } const audioContext = new AudioContext({ - sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLE_RATE, + sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, latencyHint: 'interactive' }); From 196d0a8fe8db9e07dd06db6cdeab8878fc9936fc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 18 Aug 2023 12:06:01 +0200 Subject: [PATCH 035/607] voice - log error in output --- src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index a2a1d3218e9..71c72a1c814 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -39,6 +39,7 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; if (!modulePath) { + this.logService.error(`[voice] transcribe(${channelData.length}): Voice recognition not yet supported`); throw new Error('Voice recognition not yet supported!'); } From 182970467566881d16ffce964188b0a5aed6741a Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 12:17:35 +0200 Subject: [PATCH 036/607] cleaning the code --- .../src/configuration/configuration.ts | 6 ++++++ .../src/languageFeatures/diagnostics.ts | 17 +++-------------- .../src/typescriptServiceClient.ts | 2 +- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index cab1cf4c819..2650d015d88 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -112,6 +112,7 @@ export interface TypeScriptServiceConfiguration { readonly useSyntaxServer: SyntaxServerConfiguration; readonly webProjectWideIntellisenseEnabled: boolean; readonly webProjectWideIntellisenseSuppressSemanticErrors: boolean; + readonly enableDiagnosticsTelemetry: boolean; readonly enableProjectDiagnostics: boolean; readonly maxTsServerMemory: number; readonly enablePromptUseWorkspaceTsdk: boolean; @@ -144,6 +145,7 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu useSyntaxServer: this.readUseSyntaxServer(configuration), webProjectWideIntellisenseEnabled: this.readWebProjectWideIntellisenseEnable(configuration), webProjectWideIntellisenseSuppressSemanticErrors: this.readWebProjectWideIntellisenseSuppressSemanticErrors(configuration), + enableDiagnosticsTelemetry: this.readEnableDiagnosticsTelemetry(configuration), enableProjectDiagnostics: this.readEnableProjectDiagnostics(configuration), maxTsServerMemory: this.readMaxTsServerMemory(configuration), enablePromptUseWorkspaceTsdk: this.readEnablePromptUseWorkspaceTsdk(configuration), @@ -197,6 +199,10 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu return SyntaxServerConfiguration.Never; } + protected readEnableDiagnosticsTelemetry(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('typescript.enableDiagnosticsTelemetry', false); + } + protected readEnableProjectDiagnostics(configuration: vscode.WorkspaceConfiguration): boolean { return configuration.get('typescript.tsserver.experimental.enableProjectDiagnostics', false); } diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 4331278c4ff..d3694cafdb1 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -10,7 +10,7 @@ import { Disposable } from '../utils/dispose'; import { ResourceMap } from '../utils/resourceMap'; import { TelemetryReporter } from '../logging/telemetry'; import { URI } from 'vscode-uri'; -import { diff } from 'semver'; +import { TypeScriptServiceConfiguration } from '../configuration/configuration'; function diagnosticsEquals(a: vscode.Diagnostic, b: vscode.Diagnostic): boolean { if (a === b) { @@ -151,18 +151,6 @@ class DiagnosticSettings { } } -/* -const diagnostics = this.getDiagnostics(document.uri); - const diagnoticCodes = diagnostics.reduce(function (result: number[], d: vscode.Diagnostic) { - const code = d.code; - if (typeof code === 'string' || typeof code === 'number') { - result.push(Number(code)); - } else if (code !== undefined) { - result.push(Number(code.value)); - } - return result; - }, []); - */ class DiagnosticsTelemetryManager extends Disposable { private readonly _timeOutDiagnosticMaps = new Map(); @@ -245,6 +233,7 @@ export class DiagnosticsManager extends Disposable { constructor( owner: string, + configuration: TypeScriptServiceConfiguration, telemetryReporter: TelemetryReporter, onCaseInsensitiveFileSystem: boolean ) { @@ -253,7 +242,7 @@ export class DiagnosticsManager extends Disposable { this._pendingUpdates = new ResourceMap(undefined, { onCaseInsensitiveFileSystem }); this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); - if (Math.random() * 1000 <= 1) { + if (Math.random() * 1000 <= 1 || configuration.enableDiagnosticsTelemetry) { this._register(new DiagnosticsTelemetryManager(telemetryReporter, this.getDiagnostics)); } } diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index c165f82db40..e00bed60b3f 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -212,7 +212,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType } return this.apiVersion.fullVersionString; }); - this.diagnosticsManager = new DiagnosticsManager('typescript', this.telemetryReporter, onCaseInsenitiveFileSystem); + this.diagnosticsManager = new DiagnosticsManager('typescript', this._configuration, this.telemetryReporter, onCaseInsenitiveFileSystem); this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory); this._register(this.pluginManager.onDidUpdateConfig(update => { From 3e937b6a20022ee622ed53bf2786de6e3695f5d4 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 12:55:25 +0200 Subject: [PATCH 037/607] cleaning the code --- .../src/languageFeatures/diagnostics.ts | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index d3694cafdb1..cfd73512209 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -153,9 +153,9 @@ class DiagnosticSettings { class DiagnosticsTelemetryManager extends Disposable { - private readonly _timeOutDiagnosticMaps = new Map(); - private readonly _diagnosticsSnapshot = new Map(); private readonly _diagnosticCodesMap = new Map(); + private readonly _diagnosticTimeoutsMap = new Map(); + private readonly _diagnosticSnapshotsMap = new Map(); constructor( private readonly _telemetryReporter: TelemetryReporter, @@ -164,28 +164,37 @@ class DiagnosticsTelemetryManager extends Disposable { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { if (e.document.languageId === 'typescript') { - clearTimeout(this._timeOutDiagnosticMaps.get(e.document.uri)); - const timeOut = setTimeout(() => { this._updateDiagnosticCodes(e.document.uri); }, 5000); - this._timeOutDiagnosticMaps.set(e.document.uri, timeOut); + this._updateDiagnosticCodesAfterTimeout(e.document.uri, 10000); } })); this._register(vscode.workspace.onDidOpenTextDocument(e => { if (e.languageId === 'typescript') { - this._timeOutDiagnosticMaps.set(e.uri, undefined); + this._updateDiagnosticCodesAfterTimeout(e.uri, 10000); } })); this._register(vscode.workspace.onDidCloseTextDocument(e => { if (e.languageId === 'typescript') { - this._timeOutDiagnosticMaps.delete(e.uri); + this._diagnosticTimeoutsMap.delete(e.uri); } })); + const activeUri = vscode.window.activeTextEditor?.document.uri; + this._updateDiagnosticCodesAfterTimeout(activeUri, 10000); this._sendTelemetryEvent(); } + private _updateDiagnosticCodesAfterTimeout(uri: URI | undefined, timeoutInMs: number) { + if (!uri) { + return; + } + clearTimeout(this._diagnosticTimeoutsMap.get(uri)); + const timeout = setTimeout(() => { this._updateDiagnosticCodes(uri); }, timeoutInMs); + this._diagnosticTimeoutsMap.set(uri, timeout); + } + private _updateDiagnosticCodes(uri: URI) { - const previousDiagnostics = this._diagnosticsSnapshot.get(uri); + const previousDiagnostics = this._diagnosticSnapshotsMap.get(uri); const currentDiagnostics = this._getDiagnostics(uri); - this._diagnosticsSnapshot.set(uri, currentDiagnostics); + this._diagnosticSnapshotsMap.set(uri, currentDiagnostics); const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => JSON.stringify(diagnostic) === JSON.stringify(previousDiagnostic))); diagnosticsDiff.forEach((diagnostic) => { const code = diagnostic.code; @@ -219,7 +228,7 @@ class DiagnosticsTelemetryManager extends Disposable { }); } this._sendTelemetryEvent(); - }, 5 * 60 * 1000); + }, 5 * 60 * 1000); // 5 minutes } } @@ -243,7 +252,7 @@ export class DiagnosticsManager extends Disposable { this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); if (Math.random() * 1000 <= 1 || configuration.enableDiagnosticsTelemetry) { - this._register(new DiagnosticsTelemetryManager(telemetryReporter, this.getDiagnostics)); + this._register(new DiagnosticsTelemetryManager(telemetryReporter, this.getDiagnostics.bind(this))); } } From d56bd87a31ae8b268553e3b128278411d1279f1a Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 14:35:17 +0200 Subject: [PATCH 038/607] using instead the vscode Uri instead of the entity from vscode-uri --- .../src/languageFeatures/diagnostics.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index cfd73512209..8897d6ac96a 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -9,7 +9,6 @@ import * as arrays from '../utils/arrays'; import { Disposable } from '../utils/dispose'; import { ResourceMap } from '../utils/resourceMap'; import { TelemetryReporter } from '../logging/telemetry'; -import { URI } from 'vscode-uri'; import { TypeScriptServiceConfiguration } from '../configuration/configuration'; function diagnosticsEquals(a: vscode.Diagnostic, b: vscode.Diagnostic): boolean { @@ -154,12 +153,12 @@ class DiagnosticSettings { class DiagnosticsTelemetryManager extends Disposable { private readonly _diagnosticCodesMap = new Map(); - private readonly _diagnosticTimeoutsMap = new Map(); - private readonly _diagnosticSnapshotsMap = new Map(); + private readonly _diagnosticTimeoutsMap = new Map(); + private readonly _diagnosticSnapshotsMap = new Map(); constructor( private readonly _telemetryReporter: TelemetryReporter, - private readonly _getDiagnostics: (uri: URI) => readonly vscode.Diagnostic[] + private readonly _getDiagnostics: (uri: vscode.Uri) => readonly vscode.Diagnostic[] ) { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { @@ -182,7 +181,7 @@ class DiagnosticsTelemetryManager extends Disposable { this._sendTelemetryEvent(); } - private _updateDiagnosticCodesAfterTimeout(uri: URI | undefined, timeoutInMs: number) { + private _updateDiagnosticCodesAfterTimeout(uri: vscode.Uri | undefined, timeoutInMs: number) { if (!uri) { return; } @@ -191,7 +190,7 @@ class DiagnosticsTelemetryManager extends Disposable { this._diagnosticTimeoutsMap.set(uri, timeout); } - private _updateDiagnosticCodes(uri: URI) { + private _updateDiagnosticCodes(uri: vscode.Uri) { const previousDiagnostics = this._diagnosticSnapshotsMap.get(uri); const currentDiagnostics = this._getDiagnostics(uri); this._diagnosticSnapshotsMap.set(uri, currentDiagnostics); From 1f86585b9bc07bd5e3544e72fe2f863be7b34fb9 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 14:37:30 +0200 Subject: [PATCH 039/607] remove the setting of 10000 for updating the diagnostics --- .../src/languageFeatures/diagnostics.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 8897d6ac96a..cb097121267 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -163,12 +163,12 @@ class DiagnosticsTelemetryManager extends Disposable { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { if (e.document.languageId === 'typescript') { - this._updateDiagnosticCodesAfterTimeout(e.document.uri, 10000); + this._updateDiagnosticCodesAfterTimeout(e.document.uri); } })); this._register(vscode.workspace.onDidOpenTextDocument(e => { if (e.languageId === 'typescript') { - this._updateDiagnosticCodesAfterTimeout(e.uri, 10000); + this._updateDiagnosticCodesAfterTimeout(e.uri); } })); this._register(vscode.workspace.onDidCloseTextDocument(e => { @@ -177,16 +177,16 @@ class DiagnosticsTelemetryManager extends Disposable { } })); const activeUri = vscode.window.activeTextEditor?.document.uri; - this._updateDiagnosticCodesAfterTimeout(activeUri, 10000); + this._updateDiagnosticCodesAfterTimeout(activeUri); this._sendTelemetryEvent(); } - private _updateDiagnosticCodesAfterTimeout(uri: vscode.Uri | undefined, timeoutInMs: number) { + private _updateDiagnosticCodesAfterTimeout(uri: vscode.Uri | undefined) { if (!uri) { return; } clearTimeout(this._diagnosticTimeoutsMap.get(uri)); - const timeout = setTimeout(() => { this._updateDiagnosticCodes(uri); }, timeoutInMs); + const timeout = setTimeout(() => { this._updateDiagnosticCodes(uri); }, 10000); this._diagnosticTimeoutsMap.set(uri, timeout); } From 03f09a238ce1a50d2e55207fd217b23281e29b04 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 14:40:14 +0200 Subject: [PATCH 040/607] adding a comment --- .../src/languageFeatures/diagnostics.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index cb097121267..8ee7322975c 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -250,6 +250,7 @@ export class DiagnosticsManager extends Disposable { this._pendingUpdates = new ResourceMap(undefined, { onCaseInsensitiveFileSystem }); this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); + // Here we are selecting only 1 user out of 1000 to send telemetry diagnostics if (Math.random() * 1000 <= 1 || configuration.enableDiagnosticsTelemetry) { this._register(new DiagnosticsTelemetryManager(telemetryReporter, this.getDiagnostics.bind(this))); } From afcac53ae9f752804e39ced694a7d213c7d63b05 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 18 Aug 2023 14:59:44 +0200 Subject: [PATCH 041/607] review comments --- .../src/languageFeatures/diagnostics.ts | 39 +++++++------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 8ee7322975c..fb09cee0b04 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -153,8 +153,8 @@ class DiagnosticSettings { class DiagnosticsTelemetryManager extends Disposable { private readonly _diagnosticCodesMap = new Map(); - private readonly _diagnosticTimeoutsMap = new Map(); private readonly _diagnosticSnapshotsMap = new Map(); + private _timeout: NodeJS.Timeout | undefined; constructor( private readonly _telemetryReporter: TelemetryReporter, @@ -163,31 +163,21 @@ class DiagnosticsTelemetryManager extends Disposable { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { if (e.document.languageId === 'typescript') { - this._updateDiagnosticCodesAfterTimeout(e.document.uri); + this._updateDiagnosticCodesAfterTimeoutForUri(e.document.uri); } })); - this._register(vscode.workspace.onDidOpenTextDocument(e => { - if (e.languageId === 'typescript') { - this._updateDiagnosticCodesAfterTimeout(e.uri); - } - })); - this._register(vscode.workspace.onDidCloseTextDocument(e => { - if (e.languageId === 'typescript') { - this._diagnosticTimeoutsMap.delete(e.uri); - } - })); - const activeUri = vscode.window.activeTextEditor?.document.uri; - this._updateDiagnosticCodesAfterTimeout(activeUri); - this._sendTelemetryEvent(); + this._updateAllDiagnosticCodesAfterTimeout(); + this._registerTelemetryEventEmitter(); } - private _updateDiagnosticCodesAfterTimeout(uri: vscode.Uri | undefined) { - if (!uri) { - return; - } - clearTimeout(this._diagnosticTimeoutsMap.get(uri)); - const timeout = setTimeout(() => { this._updateDiagnosticCodes(uri); }, 10000); - this._diagnosticTimeoutsMap.set(uri, timeout); + private _updateAllDiagnosticCodesAfterTimeout() { + const uris = vscode.workspace.textDocuments.map(doc => doc.uri); + uris.forEach(uri => setTimeout(() => { this._updateDiagnosticCodes(uri); }, 10000)); + } + + private _updateDiagnosticCodesAfterTimeoutForUri(uri: vscode.Uri) { + clearTimeout(this._timeout); + this._timeout = setTimeout(() => { this._updateDiagnosticCodes(uri); }, 10000); } private _updateDiagnosticCodes(uri: vscode.Uri) { @@ -205,8 +195,8 @@ class DiagnosticsTelemetryManager extends Disposable { }); } - private _sendTelemetryEvent() { - setTimeout(() => { + private _registerTelemetryEventEmitter() { + setInterval(() => { if (this._diagnosticCodesMap.size > 0) { let diagnosticCodes = ''; this._diagnosticCodesMap.forEach((value, key) => { @@ -226,7 +216,6 @@ class DiagnosticsTelemetryManager extends Disposable { diagnoticCodes: diagnosticCodes }); } - this._sendTelemetryEvent(); }, 5 * 60 * 1000); // 5 minutes } } From b0d44b6b045b990bd75112982792fc25cfd9e6fd Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 18 Aug 2023 15:42:27 +0200 Subject: [PATCH 042/607] voice - wire into chat widget as action --- src/vs/workbench/contrib/chat/browser/chat.ts | 2 + .../contrib/chat/browser/chatInputPart.ts | 2 +- .../contrib/chat/browser/chatWidget.ts | 9 ++ .../actions/chatVoiceInputActions.ts | 140 ++++++++++++++++++ .../electron-sandbox/chat.contribution.ts | 48 +----- 5 files changed, 154 insertions(+), 47 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 59e1f4181ef..3ff45ac4f9c 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -70,6 +70,7 @@ export type IChatWidgetViewContext = IChatViewViewContext | IChatResourceViewCon export interface IChatWidget { readonly onDidChangeViewModel: Event; + readonly onDidAcceptInput: Event; readonly viewContext: IChatWidgetViewContext; readonly viewModel: IChatViewModel | undefined; readonly inputEditor: ICodeEditor; @@ -79,6 +80,7 @@ export interface IChatWidget { focus(item: ChatTreeItem): void; moveFocus(item: ChatTreeItem, type: 'next' | 'previous'): void; getFocus(): ChatTreeItem | undefined; + updateInput(query?: string): void; acceptInput(query?: string): void; focusLastMessage(): void; focusInput(): void; diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 6dce4e1d7d1..2b05567c7b1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -138,7 +138,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.setHistoryNavigationEnablement(true); } - private setValue(value: string): void { + setValue(value: string): void { this.inputEditor.setValue(value); // always leave cursor at the end this.inputEditor.setPosition({ lineNumber: 1, column: value.length + 1 }); diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 9f623e3381e..aca5a1d961f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -61,6 +61,9 @@ export class ChatWidget extends Disposable implements IChatWidget { private _onDidClear = this._register(new Emitter()); readonly onDidClear = this._onDidClear.event; + private _onDidAcceptInput = this._register(new Emitter()); + readonly onDidAcceptInput = this._onDidAcceptInput.event; + private tree!: WorkbenchObjectTree; private renderer!: ChatListItemRenderer; @@ -432,8 +435,14 @@ export class ChatWidget extends Disposable implements IChatWidget { this.tree.domFocus(); } + updateInput(value = ''): void { + this.inputPart.setValue(value); + } + async acceptInput(query?: string | IChatReplyFollowup): Promise { if (this.viewModel) { + this._onDidAcceptInput.fire(); + const editorValue = this.inputPart.inputEditor.getValue(); this._chatAccessibilityService.acceptRequest(); const input = query ?? editorValue; diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts new file mode 100644 index 00000000000..88987d229f8 --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -0,0 +1,140 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Codicon } from 'vs/base/common/codicons'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IChatWidget } from 'vs/workbench/contrib/chat/browser/chat'; +import { CONTEXT_CHAT_REQUEST_IN_PROGRESS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; +import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; + +const CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS = new RawContextKey('chatVoiceInputInProgress', false, { type: 'boolean', description: localize('interactiveSessionVoiceInputInProgress', "True when there is voice input for chat in progress.") }); + +interface IChatVoiceInputActionContext { + readonly widget: IChatWidget; + readonly inputValue?: string; +} + +function isVoiceInputActionContext(thing: unknown): thing is IChatVoiceInputActionContext { + return typeof thing === 'object' && thing !== null && 'widget' in thing; +} + +class ChatVoiceInputSession { + + private static instance: ChatVoiceInputSession | undefined = undefined; + static getInstance(instantiationService: IInstantiationService): ChatVoiceInputSession { + if (!ChatVoiceInputSession.instance) { + ChatVoiceInputSession.instance = instantiationService.createInstance(ChatVoiceInputSession); + } + + return ChatVoiceInputSession.instance; + } + + private chatVoiceInputInProgressKey = CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.bindTo(this.contextKeyService); + private currentChatVoiceInputSession: DisposableStore | undefined = undefined; + + constructor( + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IWorkbenchVoiceRecognitionService private readonly voiceRecognitionService: IWorkbenchVoiceRecognitionService + ) { } + + start(context: IChatVoiceInputActionContext): void { + this.stop(); + + this.chatVoiceInputInProgressKey.set(true); + this.currentChatVoiceInputSession = new DisposableStore(); + + const cts = new CancellationTokenSource(); + this.currentChatVoiceInputSession.add(toDisposable(() => cts.dispose(true))); + + context.widget.focusInput(); + + this.currentChatVoiceInputSession.add(this.voiceRecognitionService.transcribe(cts.token)(text => { + if (text) { + context.widget.updateInput(text); + } + })); + + this.currentChatVoiceInputSession.add(context.widget.onDidAcceptInput(() => { + this.stop(); + })); + } + + stop(): void { + if (!this.currentChatVoiceInputSession) { + return; + } + + this.currentChatVoiceInputSession.dispose(); + this.currentChatVoiceInputSession = undefined; + + this.chatVoiceInputInProgressKey.set(false); + } +} + +class StartChatVoiceInputAction extends Action2 { + static readonly ID = 'workbench.action.chat.startVoiceInput'; + + constructor() { + super({ + id: StartChatVoiceInputAction.ID, + title: { + value: localize('interactive.voiceInput.label', "Start Voice Input"), + original: 'Start Voice Input' + }, + icon: Codicon.record, + menu: { + id: MenuId.ChatExecute, + when: ContextKeyExpr.and(CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.negate(), CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()), + group: 'navigation', + order: -1 + } + }); + } + + run(accessor: ServicesAccessor, ...args: any[]) { + const context = args[0]; + if (!isVoiceInputActionContext(context)) { + return; + } + + ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).start(context); + } +} + +class StopChatVoiceInputAction extends Action2 { + static readonly ID = 'workbench.action.chat.stopVoiceInput'; + + constructor() { + super({ + id: StopChatVoiceInputAction.ID, + title: { + value: localize('interactive.stopVoiceInput.label', "Stop Voice Input"), + original: 'Stop Voice Input' + }, + icon: Codicon.stop, + menu: { + id: MenuId.ChatExecute, + when: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS, + group: 'navigation', + order: -1 + } + }); + } + + run(accessor: ServicesAccessor) { + ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).stop(); + } +} + +export function registerChatVoiceInputActions() { + registerAction2(StartChatVoiceInputAction); + registerAction2(StopChatVoiceInputAction); +} diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts index 45bb21c29ef..5d49a2d0625 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -3,50 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toAction } from 'vs/base/common/actions'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { INotificationService, NotificationPriority, Severity } from 'vs/platform/notification/common/notification'; -import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; +import { registerChatVoiceInputActions } from 'vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions'; -let activeVoiceTranscription: DisposableStore | undefined; - -function stopVoiceTranscription() { - activeVoiceTranscription?.dispose(); - activeVoiceTranscription = undefined; -} - -CommandsRegistry.registerCommand('workbench.action.toggleVoiceTranscription', async services => { - if (activeVoiceTranscription) { - stopVoiceTranscription(); - } else { - const voiceRecognitionService = services.get(IWorkbenchVoiceRecognitionService); - const notificationService = services.get(INotificationService); - - activeVoiceTranscription = new DisposableStore(); - - const cts = new CancellationTokenSource(); - activeVoiceTranscription.add(toDisposable(() => cts.dispose(true))); - - const voiceTranscriptionNotification = notificationService.notify({ - severity: Severity.Info, - priority: NotificationPriority.URGENT, - sticky: true, - message: 'Listening...', - actions: { - primary: [ - toAction({ id: 'stopVoiceTranscription', label: 'Stop', run: () => stopVoiceTranscription() }) - ] - } - }); - - activeVoiceTranscription.add(toDisposable(() => voiceTranscriptionNotification.close())); - - activeVoiceTranscription.add(voiceRecognitionService.transcribe(cts.token)(text => { - if (text) { - voiceTranscriptionNotification.updateMessage(text); - } - })); - } -}); +registerChatVoiceInputActions(); From 4fb3cd66f56b1da667510da340b996836547959c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 19 Aug 2023 11:32:17 +0200 Subject: [PATCH 043/607] voice - better actions and icons --- .../contrib/chat/browser/chatInputPart.ts | 16 +++++++++------- .../actions/chatVoiceInputActions.ts | 8 ++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 2b05567c7b1..d0982610acd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -40,7 +40,7 @@ const $ = dom.$; const INPUT_EDITOR_MAX_HEIGHT = 250; export class ChatInputPart extends Disposable implements IHistoryNavigationWidget { - public static readonly INPUT_SCHEME = 'chatSessionInput'; + static readonly INPUT_SCHEME = 'chatSessionInput'; private static _counter = 0; private _onDidChangeHeight = this._register(new Emitter()); @@ -64,7 +64,9 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private _inputEditor!: CodeEditorWidget; private _inputEditorElement!: HTMLElement; - public get inputEditor() { + private toolbar!: MenuWorkbenchToolBar; + + get inputEditor() { return this._inputEditor; } @@ -74,7 +76,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private inputEditorHasText: IContextKey; private providerId: string | undefined; - public readonly inputUri = URI.parse(`${ChatInputPart.INPUT_SCHEME}:input-${ChatInputPart._counter++}`); + readonly inputUri = URI.parse(`${ChatInputPart.INPUT_SCHEME}:input-${ChatInputPart._counter++}`); constructor( // private readonly editorOptions: ChatEditorOptions, // TODO this should be used @@ -233,13 +235,13 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this._onDidBlur.fire(); })); - const toolbar = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, inputContainer, MenuId.ChatExecute, { + this.toolbar = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, inputContainer, MenuId.ChatExecute, { menuOptions: { shouldForwardArgs: true } })); - toolbar.getElement().classList.add('interactive-execute-toolbar'); - toolbar.context = { widget }; + this.toolbar.getElement().classList.add('interactive-execute-toolbar'); + this.toolbar.context = { widget }; if (this.options.renderStyle === 'compact') { const toolbarSide = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, inputAndSideToolbar, MenuId.ChatInputSide, { @@ -290,7 +292,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const editorBorder = 2; const editorPadding = 8; - const executeToolbarWidth = 25; + const executeToolbarWidth = this.toolbar.getItemsWidth(); const sideToolbarWidth = this.options.renderStyle === 'compact' ? 20 : 0; const initialEditorScrollWidth = this._inputEditor.getScrollWidth(); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 88987d229f8..4963c2dce0f 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -9,10 +9,10 @@ import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { IChatWidget } from 'vs/workbench/contrib/chat/browser/chat'; -import { CONTEXT_CHAT_REQUEST_IN_PROGRESS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; const CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS = new RawContextKey('chatVoiceInputInProgress', false, { type: 'boolean', description: localize('interactiveSessionVoiceInputInProgress', "True when there is voice input for chat in progress.") }); @@ -92,7 +92,7 @@ class StartChatVoiceInputAction extends Action2 { icon: Codicon.record, menu: { id: MenuId.ChatExecute, - when: ContextKeyExpr.and(CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.negate(), CONTEXT_CHAT_REQUEST_IN_PROGRESS.negate()), + when: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.negate(), group: 'navigation', order: -1 } @@ -119,7 +119,7 @@ class StopChatVoiceInputAction extends Action2 { value: localize('interactive.stopVoiceInput.label', "Stop Voice Input"), original: 'Stop Voice Input' }, - icon: Codicon.stop, + icon: spinningLoading, menu: { id: MenuId.ChatExecute, when: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS, From d91e1a8ba3be4dad18dc8f3a3de62638f4347be8 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 19 Aug 2023 11:47:26 +0200 Subject: [PATCH 044/607] voice - indicate when ready to record --- .../actions/chatVoiceInputActions.ts | 21 +++++++++++--- .../workbenchVoiceRecognitionService.ts | 29 ++++++++++++------- 2 files changed, 36 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 4963c2dce0f..ed0dc175175 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -15,7 +15,8 @@ import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { IChatWidget } from 'vs/workbench/contrib/chat/browser/chat'; import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; -const CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS = new RawContextKey('chatVoiceInputInProgress', false, { type: 'boolean', description: localize('interactiveSessionVoiceInputInProgress', "True when there is voice input for chat in progress.") }); +const CONTEXT_CHAT_VOICE_INPUT_GETTING_READY = new RawContextKey('chatVoiceInputGettingReady', false, { type: 'boolean', description: localize('chatVoiceInputGettingReady', "True when there is voice input for chat getting ready.") }); +const CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS = new RawContextKey('chatVoiceInputInProgress', false, { type: 'boolean', description: localize('chatVoiceInputInProgress', "True when there is voice input for chat in progress.") }); interface IChatVoiceInputActionContext { readonly widget: IChatWidget; @@ -38,6 +39,8 @@ class ChatVoiceInputSession { } private chatVoiceInputInProgressKey = CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.bindTo(this.contextKeyService); + private chatVoiceInputGettingReadyKey = CONTEXT_CHAT_VOICE_INPUT_GETTING_READY.bindTo(this.contextKeyService); + private currentChatVoiceInputSession: DisposableStore | undefined = undefined; constructor( @@ -45,10 +48,10 @@ class ChatVoiceInputSession { @IWorkbenchVoiceRecognitionService private readonly voiceRecognitionService: IWorkbenchVoiceRecognitionService ) { } - start(context: IChatVoiceInputActionContext): void { + async start(context: IChatVoiceInputActionContext): Promise { this.stop(); - this.chatVoiceInputInProgressKey.set(true); + this.chatVoiceInputGettingReadyKey.set(true); this.currentChatVoiceInputSession = new DisposableStore(); const cts = new CancellationTokenSource(); @@ -56,7 +59,15 @@ class ChatVoiceInputSession { context.widget.focusInput(); - this.currentChatVoiceInputSession.add(this.voiceRecognitionService.transcribe(cts.token)(text => { + const onDidTranscribe = await this.voiceRecognitionService.transcribe(cts.token); + if (cts.token.isCancellationRequested) { + return; + } + + this.chatVoiceInputGettingReadyKey.set(false); + this.chatVoiceInputInProgressKey.set(true); + + this.currentChatVoiceInputSession.add(onDidTranscribe(text => { if (text) { context.widget.updateInput(text); } @@ -75,6 +86,7 @@ class ChatVoiceInputSession { this.currentChatVoiceInputSession.dispose(); this.currentChatVoiceInputSession = undefined; + this.chatVoiceInputGettingReadyKey.set(false); this.chatVoiceInputInProgressKey.set(false); } } @@ -90,6 +102,7 @@ class StartChatVoiceInputAction extends Action2 { original: 'Start Voice Input' }, icon: Codicon.record, + precondition: CONTEXT_CHAT_VOICE_INPUT_GETTING_READY.negate(), menu: { id: MenuId.ChatExecute, when: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.negate(), diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index e2e94971c3a..cc159e4f06b 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -25,7 +25,7 @@ export interface IWorkbenchVoiceRecognitionService { * @param cancellation a cancellation token to stop transcribing and * listening to the microphone. */ - transcribe(cancellation: CancellationToken): Event; + transcribe(cancellation: CancellationToken): Promise>; } class VoiceTranscriptionWorkletNode extends AudioWorkletNode { @@ -76,16 +76,19 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit @ISharedProcessService private readonly sharedProcessService: ISharedProcessService ) { } - transcribe(cancellation: CancellationToken): Event { + async transcribe(cancellation: CancellationToken): Promise> { const onDidTranscribe = new Emitter(); cancellation.onCancellationRequested(() => onDidTranscribe.dispose()); - this.doTranscribe(onDidTranscribe, cancellation); + await this.doTranscribe(onDidTranscribe, cancellation); return onDidTranscribe.event; } - private doTranscribe(onDidTranscribe: Emitter, token: CancellationToken): void { + private doTranscribe(onDidTranscribe: Emitter, token: CancellationToken): Promise { + const recordingReady = new DeferredPromise(); + token.onCancellationRequested(() => recordingReady.complete()); + this.progressService.withProgress({ location: ProgressLocation.Window, title: localize('voiceTranscription', "Voice Transcription"), @@ -116,13 +119,16 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit const microphoneSource = audioContext.createMediaStreamSource(microphoneDevice); token.onCancellationRequested(() => { - for (const track of microphoneDevice.getTracks()) { - track.stop(); - } + try { + for (const track of microphoneDevice.getTracks()) { + track.stop(); + } - microphoneSource.disconnect(); - audioContext.close(); - recordingDone.complete(); + microphoneSource.disconnect(); + audioContext.close(); + } finally { + recordingDone.complete(); + } }); await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js').toString(true)); @@ -144,9 +150,12 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit microphoneSource.connect(voiceTranscriptionTarget); progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); + recordingReady.complete(); return recordingDone.p; }); + + return recordingReady.p; } } From c1715502dd926328198cca7b386cc8fdf34e836f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 20 Aug 2023 08:15:14 +0200 Subject: [PATCH 045/607] voice - auto accept input after a while --- .../actions/chatVoiceInputActions.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index ed0dc175175..48e4c3b3f7d 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -67,9 +67,23 @@ class ChatVoiceInputSession { this.chatVoiceInputGettingReadyKey.set(false); this.chatVoiceInputInProgressKey.set(true); + let lastText: string | undefined = undefined; + let lastTextEqualCount = 0; + this.currentChatVoiceInputSession.add(onDidTranscribe(text => { if (text) { - context.widget.updateInput(text); + if (lastText === text) { + lastTextEqualCount++; + + if (lastTextEqualCount >= 2) { + context.widget.acceptInput(); + } + } else { + lastTextEqualCount = 0; + lastText = text; + + context.widget.updateInput(text); + } } })); From 1f515f71cff8fb13f86e25a2175dbe33d44fffe3 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 11:38:31 +0200 Subject: [PATCH 046/607] setting z-index to the z-index of the content hover --- .../browser/standaloneColorPickerWidget.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts index 6d874e71d4b..a6ef1b75116 100644 --- a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts @@ -87,11 +87,10 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW static readonly ID = 'editor.contrib.standaloneColorPickerWidget'; readonly allowEditorOverflow = true; - private body: HTMLElement = document.createElement('div'); - private readonly _position: Position | undefined = undefined; private readonly _standaloneColorPickerParticipant: StandaloneColorPickerParticipant; + private _body: HTMLElement = document.createElement('div'); private _colorHover: StandaloneColorPickerHover | null = null; private _selectionSetInEditor: boolean = false; @@ -120,7 +119,7 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW endLineNumber: editorSelection.endLineNumber, endColumn: editorSelection.endColumn } : { startLineNumber: 0, endLineNumber: 0, endColumn: 0, startColumn: 0 }; - const focusTracker = this._register(dom.trackFocus(this.body)); + const focusTracker = this._register(dom.trackFocus(this._body)); this._register(focusTracker.onDidBlur(_ => { this.hide(); })); @@ -146,6 +145,7 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW this._render(result.value, result.foundInEditor); })); this._start(selection); + this._body.style.zIndex = '50'; this._editor.addContentWidget(this); } @@ -160,7 +160,7 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW } public getDomNode(): HTMLElement { - return this.body; + return this._body; } public getPosition(): IContentWidgetPosition | null { @@ -186,7 +186,7 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW public focus(): void { this._standaloneColorPickerFocused.set(true); - this.body.focus(); + this._body.focus(); } private async _start(selection: IRange) { @@ -230,11 +230,11 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW if (colorPickerWidget === undefined) { return; } - this.body.classList.add('standalone-colorpicker-body'); - this.body.style.maxHeight = Math.max(this._editor.getLayoutInfo().height / 4, 250) + 'px'; - this.body.style.maxWidth = Math.max(this._editor.getLayoutInfo().width * 0.66, 500) + 'px'; - this.body.tabIndex = 0; - this.body.appendChild(fragment); + this._body.classList.add('standalone-colorpicker-body'); + this._body.style.maxHeight = Math.max(this._editor.getLayoutInfo().height / 4, 250) + 'px'; + this._body.style.maxWidth = Math.max(this._editor.getLayoutInfo().width * 0.66, 500) + 'px'; + this._body.tabIndex = 0; + this._body.appendChild(fragment); colorPickerWidget.layout(); const colorPickerBody = colorPickerWidget.body; From 3165d7d7e9d96dc193e7d39175c7e392e460f620 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Aug 2023 12:23:25 +0200 Subject: [PATCH 047/607] voice - adopt `AbortSignal` --- .../node/voiceRecognitionService.ts | 17 +++++++++++++++-- .../actions/chatVoiceInputActions.ts | 3 ++- .../workbenchVoiceRecognitionService.ts | 2 -- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index 71c72a1c814..d2279001d75 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -45,7 +45,19 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { const now = Date.now(); - const voiceModule: { transcribe: (audioBuffer: { channelCount: 1; samplingRate: 16000; bitDepth: 16; channelData: Float32Array }, options: { language: string | 'auto'; suppressNonSpeechTokens: boolean }) => Promise } = require.__$__nodeRequire(modulePath); + const voiceModule: { + transcribe: ( + audioBuffer: { channelCount: 1; samplingRate: 16000; bitDepth: 16; channelData: Float32Array }, + options: { + language: string | 'auto'; + suppressNonSpeechTokens: boolean; + signal: AbortSignal; + } + ) => Promise; + } = require.__$__nodeRequire(modulePath); + + const abortController = new AbortController(); + cancellation.onCancellationRequested(() => abortController.abort()); const text = await voiceModule.transcribe({ samplingRate: 16000, @@ -54,7 +66,8 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { channelData }, { language: 'en', - suppressNonSpeechTokens: true + suppressNonSpeechTokens: true, + signal: abortController.signal }); this.logService.info(`[voice] transcribe(${channelData.length}): End (text: "${text}", took: ${Date.now() - now}ms)`); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 48e4c3b3f7d..2cfbcf9d255 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -6,6 +6,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; @@ -72,7 +73,7 @@ class ChatVoiceInputSession { this.currentChatVoiceInputSession.add(onDidTranscribe(text => { if (text) { - if (lastText === text) { + if (lastText && equalsIgnoreCase(text, lastText)) { lastTextEqualCount++; if (lastTextEqualCount >= 2) { diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index cc159e4f06b..397877ca471 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -61,8 +61,6 @@ class VoiceTranscriptionWorkletNode extends AudioWorkletNode { } } -// TODO@voice -// - pass cancellation down into the node module via AbortSignal export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { declare readonly _serviceBrand: undefined; From 7d2d2b92e6e8a6123cb91b0aa3a9d8bf68cd0827 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 13:16:50 +0200 Subject: [PATCH 048/607] adding the code for toggle icons in sticky scroll --- .../browser/stickyScrollController.ts | 6 +- .../browser/stickyScrollWidget.ts | 78 ++++++++++++++++++- 2 files changed, 79 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 265e0c458ff..d004bb33ea1 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -303,7 +303,11 @@ export class StickyScrollController extends Disposable implements IEditorContrib sessionStore.clear(); })); this._register(gesture.onExecute(async e => { - if (e.target.type !== MouseTargetType.OVERLAY_WIDGET || e.target.detail !== this._stickyScrollWidget.getId()) { + if ( + e.target.type !== MouseTargetType.OVERLAY_WIDGET + || e.target.detail !== this._stickyScrollWidget.getId() + || e.target.element?.classList.contains('unfold-icon') + ) { // not hovering over our widget return; } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 61914f776c7..0bec6141d1f 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -5,7 +5,8 @@ import * as dom from 'vs/base/browser/dom'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ThemeIcon } from 'vs/base/common/themables'; import 'vs/css!./stickyScroll'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; import { getColumnOfNodeOffset } from 'vs/editor/browser/viewParts/lines/viewLine'; @@ -15,6 +16,9 @@ import { Position } from 'vs/editor/common/core/position'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { CharacterMapping, RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; +import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; +import { foldingCollapsedIcon, foldingExpandedIcon } from 'vs/editor/contrib/folding/browser/foldingDecorations'; +import { FoldingModel, toggleCollapseState } from 'vs/editor/contrib/folding/browser/foldingModel'; export class StickyScrollWidgetState { constructor( @@ -28,6 +32,7 @@ const STICKY_LINE_INDEX_ATTR = 'data-sticky-line-index'; export class StickyScrollWidget extends Disposable implements IOverlayWidget { + private readonly _disposableStore = new DisposableStore(); private readonly _rootDomNode: HTMLElement = document.createElement('div'); private readonly _lineNumbersDomNode: HTMLElement = document.createElement('div'); private readonly _linesDomNodeScrollable: HTMLElement = document.createElement('div'); @@ -123,15 +128,23 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.style.width = `${layoutInfo.width - layoutInfo.minimap.minimapCanvasOuterWidth - layoutInfo.verticalScrollbarWidth}px`; } - private _renderRootNode(): void { + private async _renderRootNode(): Promise { if (!this._editor._getViewModel()) { return; } + // Folding + let foldingModel: FoldingModel | null = null; + const foldingController = FoldingController.get(this._editor); + if (foldingController) { + foldingModel = await foldingController.getFoldingModel(); + } + // Folding + const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { - const { lineNumberHTMLNode, renderedStickyLine } = this._renderChildNode(index, line, layoutInfo); + const { lineNumberHTMLNode, renderedStickyLine } = this._renderChildNode(index, line, layoutInfo, foldingModel); this._lineNumbersDomNode.appendChild(lineNumberHTMLNode); this._linesDomNode.appendChild(renderedStickyLine.domNode); this._stickyLines.push(renderedStickyLine); @@ -154,7 +167,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._editor.layoutOverlayWidget(this); } - private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { + private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); @@ -213,6 +226,63 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } lineNumberHTMLNode.appendChild(innerLineNumberHTML); + // Added for the folding toggle icons + innerLineNumberHTML.style.float = 'left'; + if (foldingModel) { + const foldingRegions = foldingModel.regions; + const indexOfLine = foldingRegions.findRange(line); + const isCollapsed = foldingRegions.isCollapsed(indexOfLine); + const startLineNumber = foldingRegions.getStartLineNumber(indexOfLine); + const isFoldingLine = line === startLineNumber; + + if (isFoldingLine) { + const divToUnfold = document.createElement('div'); + divToUnfold.style.float = 'right'; + if (isCollapsed) { + divToUnfold.className = ThemeIcon.asClassName(foldingCollapsedIcon); + } else { + divToUnfold.className = ThemeIcon.asClassName(foldingExpandedIcon); + } + divToUnfold.style.transition = 'opacity 250ms linear'; + divToUnfold.style.opacity = '0'; + divToUnfold.style.height = '0px'; + divToUnfold.style.cursor = 'default'; + + divToUnfold.classList.add('unfold-icon'); + lineNumberHTMLNode.append(divToUnfold); + + let collapsed = isCollapsed; + + this._disposableStore.add(dom.addDisposableListener(divToUnfold, dom.EventType.CLICK, () => { + console.log('line : ', line); + + const scrollTop = this._editor.getTopForLineNumber(line) + 1; + console.log('scrollTop : ', scrollTop); + toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); + collapsed = !collapsed; + // TODO: Likely a more complicated mathematical equation that involves finding the position given the new number of lines in the sticky widget + // there appears to be an error here, doesn't behave exactly as expected + // TODO: continuous rerendering of the arrow, which need not be rerendered if already in the right collapsed state + const newHeight = scrollTop; // - (collapsed ? 0 : 18); + console.log('newHeight : ', newHeight); + this._editor.setScrollTop(newHeight); + })); + this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OVER, () => { + divToUnfold.style.opacity = '1'; + divToUnfold.style.height = '18px'; + divToUnfold.style.width = '18px'; + divToUnfold.style.cursor = 'pointer'; + })); + this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OUT, () => { + divToUnfold.style.transition = 'opacity 250ms linear'; + divToUnfold.style.opacity = '0'; + divToUnfold.style.height = '0px'; + divToUnfold.style.cursor = 'default'; + })); + } + } + // Added with the folding toggle icons + this._editor.applyFontInfo(lineHTMLNode); this._editor.applyFontInfo(innerLineNumberHTML); From b289e3e7e0072ab6eec16c46e377df94e8e8c31e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Aug 2023 14:52:12 +0200 Subject: [PATCH 049/607] voice - add a command for quick voice chat --- src/vs/workbench/contrib/chat/browser/chat.ts | 1 + .../contrib/chat/browser/chatQuick.ts | 14 +++++-- .../actions/chatVoiceInputActions.ts | 40 +++++++++++++++++-- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 6d164721797..303e525c6b1 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -38,6 +38,7 @@ export interface IQuickChatService { enabled: boolean; toggle(providerId?: string, query?: string): void; focus(): void; + open(): void; close(): void; openInChatView(): void; } diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index e43c684102d..3729e4fffb7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -20,8 +20,8 @@ import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; export class QuickChatService implements IQuickChatService { readonly _serviceBrand: undefined; - _input: IQuickWidget | undefined; - _currentChat: QuickChat | undefined; + private _input: IQuickWidget | undefined; + private _currentChat: QuickChat | undefined; constructor( @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -34,7 +34,7 @@ export class QuickChatService implements IQuickChatService { } get focused(): boolean { - const widget = this._input?.widget as HTMLElement; + const widget = this._input?.widget as HTMLElement | undefined; if (!widget) { return false; } @@ -45,7 +45,13 @@ export class QuickChatService implements IQuickChatService { // If the input is already shown, hide it. This provides a toggle behavior of the quick pick if (this.focused) { this.close(); - return; + } else { + this.open(providerId, query); + } + } + open(providerId?: string, query?: string | undefined): void { + if (this.focused) { + return this.focus(); } // Check if any providers are available. If not, show nothing diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 2cfbcf9d255..ea6cec92cc8 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -13,7 +13,8 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; -import { IChatWidget } from 'vs/workbench/contrib/chat/browser/chat'; +import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; +import { IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; const CONTEXT_CHAT_VOICE_INPUT_GETTING_READY = new RawContextKey('chatVoiceInputGettingReady', false, { type: 'boolean', description: localize('chatVoiceInputGettingReady', "True when there is voice input for chat getting ready.") }); @@ -116,6 +117,7 @@ class StartChatVoiceInputAction extends Action2 { value: localize('interactive.voiceInput.label', "Start Voice Input"), original: 'Start Voice Input' }, + category: CHAT_CATEGORY, icon: Codicon.record, precondition: CONTEXT_CHAT_VOICE_INPUT_GETTING_READY.negate(), menu: { @@ -127,7 +129,7 @@ class StartChatVoiceInputAction extends Action2 { }); } - run(accessor: ServicesAccessor, ...args: any[]) { + run(accessor: ServicesAccessor, ...args: any[]): void { const context = args[0]; if (!isVoiceInputActionContext(context)) { return; @@ -147,6 +149,9 @@ class StopChatVoiceInputAction extends Action2 { value: localize('interactive.stopVoiceInput.label', "Stop Voice Input"), original: 'Stop Voice Input' }, + category: CHAT_CATEGORY, + f1: true, + precondition: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS, icon: spinningLoading, menu: { id: MenuId.ChatExecute, @@ -157,12 +162,41 @@ class StopChatVoiceInputAction extends Action2 { }); } - run(accessor: ServicesAccessor) { + run(accessor: ServicesAccessor): void { ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).stop(); } } +class StartVoiceQuickChatAction extends Action2 { + static readonly ID = 'workbench.action.chat.startVoiceQuickChat'; + + constructor() { + super({ + id: StartVoiceQuickChatAction.ID, + title: { + value: localize('interactive.startVoiceChat.label', "Start Voice Quick Chat"), + original: 'Start Voice Quick Chat' + }, + category: CHAT_CATEGORY, + f1: true + }); + } + + run(accessor: ServicesAccessor): void { + const quickChatService = accessor.get(IQuickChatService); + const chatWidgetService = accessor.get(IChatWidgetService); + + quickChatService.open(); + + const widget = chatWidgetService.lastFocusedWidget; + if (widget) { + ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).start({ widget }); + } + } +} + export function registerChatVoiceInputActions() { registerAction2(StartChatVoiceInputAction); registerAction2(StopChatVoiceInputAction); + registerAction2(StartVoiceQuickChatAction); } From 165782617525c6da5c67033245e15770937a23d4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Aug 2023 15:03:20 +0200 Subject: [PATCH 050/607] voice - allow to start voice input to focussed chat widget via command --- src/vs/workbench/contrib/chat/browser/chat.ts | 1 + src/vs/workbench/contrib/chat/browser/chatInputPart.ts | 4 ++++ src/vs/workbench/contrib/chat/browser/chatWidget.ts | 4 ++++ .../electron-sandbox/actions/chatVoiceInputActions.ts | 10 ++++++++-- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 303e525c6b1..7450fb52efa 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -94,6 +94,7 @@ export interface IChatWidget { acceptInput(query?: string): void; focusLastMessage(): void; focusInput(): void; + hasInputFocus(): boolean; getSlashCommands(): Promise; getCodeBlockInfoForEditor(uri: URI): IChatCodeBlockInfo | undefined; getCodeBlockInfosForResponse(response: IChatResponseViewModel): IChatCodeBlockInfo[]; diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index d0982610acd..df657d54439 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -150,6 +150,10 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this._inputEditor.focus(); } + hasFocus(): boolean { + return this._inputEditor.hasWidgetFocus(); + } + async acceptInput(query?: string | IChatReplyFollowup): Promise { const editorValue = this._inputEditor.getValue(); if (!query && editorValue) { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index aca5a1d961f..053278d5af6 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -182,6 +182,10 @@ export class ChatWidget extends Disposable implements IChatWidget { this.inputPart.focus(); } + hasInputFocus(): boolean { + return this.inputPart.hasFocus(); + } + moveFocus(item: ChatTreeItem, type: 'next' | 'previous'): void { const items = this.viewModel?.getItems(); if (!items) { diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index ea6cec92cc8..9e3058c7405 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -130,9 +130,15 @@ class StartChatVoiceInputAction extends Action2 { } run(accessor: ServicesAccessor, ...args: any[]): void { - const context = args[0]; + const chatWidgetService = accessor.get(IChatWidgetService); + + let context = args[0]; if (!isVoiceInputActionContext(context)) { - return; + if (chatWidgetService.lastFocusedWidget?.hasInputFocus()) { + context = { widget: chatWidgetService.lastFocusedWidget }; + } else { + return; + } } ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).start(context); From 76cc0881d31d6d8869eb4677c1777741e0401535 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 16:24:57 +0200 Subject: [PATCH 051/607] using instead the uri string --- .../src/languageFeatures/diagnostics.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index fb09cee0b04..b55ed3af3d8 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -153,7 +153,7 @@ class DiagnosticSettings { class DiagnosticsTelemetryManager extends Disposable { private readonly _diagnosticCodesMap = new Map(); - private readonly _diagnosticSnapshotsMap = new Map(); + private readonly _diagnosticSnapshotsMap = new Map(); private _timeout: NodeJS.Timeout | undefined; constructor( @@ -181,9 +181,10 @@ class DiagnosticsTelemetryManager extends Disposable { } private _updateDiagnosticCodes(uri: vscode.Uri) { - const previousDiagnostics = this._diagnosticSnapshotsMap.get(uri); + const uriString = uri.toString(); + const previousDiagnostics = this._diagnosticSnapshotsMap.get(uriString); const currentDiagnostics = this._getDiagnostics(uri); - this._diagnosticSnapshotsMap.set(uri, currentDiagnostics); + this._diagnosticSnapshotsMap.set(uriString, currentDiagnostics); const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => JSON.stringify(diagnostic) === JSON.stringify(previousDiagnostic))); diagnosticsDiff.forEach((diagnostic) => { const code = diagnostic.code; From cf21c24624a62158dfb538fb726a8da3531684b5 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 16:48:48 +0200 Subject: [PATCH 052/607] updating all the diagnostics after the timeout --- .../src/languageFeatures/diagnostics.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index b55ed3af3d8..f98c7998e3d 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -163,7 +163,7 @@ class DiagnosticsTelemetryManager extends Disposable { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { if (e.document.languageId === 'typescript') { - this._updateDiagnosticCodesAfterTimeoutForUri(e.document.uri); + this._updateAllDiagnosticCodesAfterTimeout(); } })); this._updateAllDiagnosticCodesAfterTimeout(); @@ -171,13 +171,9 @@ class DiagnosticsTelemetryManager extends Disposable { } private _updateAllDiagnosticCodesAfterTimeout() { - const uris = vscode.workspace.textDocuments.map(doc => doc.uri); - uris.forEach(uri => setTimeout(() => { this._updateDiagnosticCodes(uri); }, 10000)); - } - - private _updateDiagnosticCodesAfterTimeoutForUri(uri: vscode.Uri) { clearTimeout(this._timeout); - this._timeout = setTimeout(() => { this._updateDiagnosticCodes(uri); }, 10000); + const uris = vscode.workspace.textDocuments.map(doc => doc.uri); + this._timeout = setTimeout(() => { uris.forEach((uri) => this._updateDiagnosticCodes(uri)); }, 5000); } private _updateDiagnosticCodes(uri: vscode.Uri) { @@ -185,7 +181,9 @@ class DiagnosticsTelemetryManager extends Disposable { const previousDiagnostics = this._diagnosticSnapshotsMap.get(uriString); const currentDiagnostics = this._getDiagnostics(uri); this._diagnosticSnapshotsMap.set(uriString, currentDiagnostics); - const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => JSON.stringify(diagnostic) === JSON.stringify(previousDiagnostic))); + const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => { + return diagnostic === previousDiagnostic; + })); diagnosticsDiff.forEach((diagnostic) => { const code = diagnostic.code; if (typeof code === 'string' || typeof code === 'number') { From 8db8d30dfa91585b75632f1d32747bb542a993d6 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 16:56:13 +0200 Subject: [PATCH 053/607] using the utility method equals --- .../src/languageFeatures/diagnostics.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index f98c7998e3d..57f1d257bb2 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -10,6 +10,7 @@ import { Disposable } from '../utils/dispose'; import { ResourceMap } from '../utils/resourceMap'; import { TelemetryReporter } from '../logging/telemetry'; import { TypeScriptServiceConfiguration } from '../configuration/configuration'; +import { equals } from '../utils/objects'; function diagnosticsEquals(a: vscode.Diagnostic, b: vscode.Diagnostic): boolean { if (a === b) { @@ -181,9 +182,7 @@ class DiagnosticsTelemetryManager extends Disposable { const previousDiagnostics = this._diagnosticSnapshotsMap.get(uriString); const currentDiagnostics = this._getDiagnostics(uri); this._diagnosticSnapshotsMap.set(uriString, currentDiagnostics); - const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => { - return diagnostic === previousDiagnostic; - })); + const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => equals(diagnostic, previousDiagnostic))); diagnosticsDiff.forEach((diagnostic) => { const code = diagnostic.code; if (typeof code === 'string' || typeof code === 'number') { From fb4b533d9352f32297ae77b5ba6d7485bbbf69aa Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 17:01:53 +0200 Subject: [PATCH 054/607] extracting the method _increaseDiagnosticCodeCount to avoid duplication --- .../src/languageFeatures/diagnostics.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 57f1d257bb2..0743d34f0e2 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -177,6 +177,13 @@ class DiagnosticsTelemetryManager extends Disposable { this._timeout = setTimeout(() => { uris.forEach((uri) => this._updateDiagnosticCodes(uri)); }, 5000); } + private _increaseDiagnosticCodeCount(code: string | number | undefined) { + if (code === undefined) { + return; + } + this._diagnosticCodesMap.set(Number(code), (this._diagnosticCodesMap.get(Number(code)) || 0) + 1); + } + private _updateDiagnosticCodes(uri: vscode.Uri) { const uriString = uri.toString(); const previousDiagnostics = this._diagnosticSnapshotsMap.get(uriString); @@ -185,11 +192,7 @@ class DiagnosticsTelemetryManager extends Disposable { const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => equals(diagnostic, previousDiagnostic))); diagnosticsDiff.forEach((diagnostic) => { const code = diagnostic.code; - if (typeof code === 'string' || typeof code === 'number') { - this._diagnosticCodesMap.set(Number(code), (this._diagnosticCodesMap.get(Number(code)) || 0) + 1); - } else if (code !== undefined) { - this._diagnosticCodesMap.set(Number(code.value), (this._diagnosticCodesMap.get(Number(code.value)) || 0) + 1); - } + this._increaseDiagnosticCodeCount(typeof code === 'string' || typeof code === 'number' ? code : code?.value); }); } From 3eaaba268f2b2e1caa03cdb668cf081e701f7788 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Aug 2023 17:36:53 +0200 Subject: [PATCH 055/607] voice - show error notification on error --- .../workbenchVoiceRecognitionService.ts | 116 ++++++++++-------- 1 file changed, 63 insertions(+), 53 deletions(-) diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 397877ca471..3a10c4afa7b 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -12,6 +12,7 @@ import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/ import { DeferredPromise } from 'vs/base/common/async'; import { FileAccess } from 'vs/base/common/network'; import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; +import { INotificationService } from 'vs/platform/notification/common/notification'; export const IWorkbenchVoiceRecognitionService = createDecorator('workbenchVoiceRecognitionService'); @@ -61,6 +62,8 @@ class VoiceTranscriptionWorkletNode extends AudioWorkletNode { } } +// TODO@voice +// - add native module test to ensure module loads export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { declare readonly _serviceBrand: undefined; @@ -71,7 +74,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit constructor( @IProgressService private readonly progressService: IProgressService, - @ISharedProcessService private readonly sharedProcessService: ISharedProcessService + @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, + @INotificationService private readonly notificationService: INotificationService ) { } async transcribe(cancellation: CancellationToken): Promise> { @@ -92,65 +96,71 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit title: localize('voiceTranscription', "Voice Transcription"), }, async progress => { const recordingDone = new DeferredPromise(); + try { + progress.report({ message: localize('voiceTranscriptionGettingReady', "Getting microphone ready...") }); - progress.report({ message: localize('voiceTranscriptionGettingReady', "Getting microphone ready...") }); - - const microphoneDevice = await navigator.mediaDevices.getUserMedia({ - audio: { - sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, - sampleSize: WorkbenchVoiceRecognitionService.AUDIO_BIT_DEPTH, - channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - autoGainControl: true, - noiseSuppression: true - } - }); - - if (token.isCancellationRequested) { - return; - } - - const audioContext = new AudioContext({ - sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, - latencyHint: 'interactive' - }); - - const microphoneSource = audioContext.createMediaStreamSource(microphoneDevice); - - token.onCancellationRequested(() => { - try { - for (const track of microphoneDevice.getTracks()) { - track.stop(); + const microphoneDevice = await navigator.mediaDevices.getUserMedia({ + audio: { + sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, + sampleSize: WorkbenchVoiceRecognitionService.AUDIO_BIT_DEPTH, + channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, + autoGainControl: true, + noiseSuppression: true } + }); - microphoneSource.disconnect(); - audioContext.close(); - } finally { - recordingDone.complete(); + if (token.isCancellationRequested) { + return; } - }); - await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js').toString(true)); + const audioContext = new AudioContext({ + sampleRate: WorkbenchVoiceRecognitionService.AUDIO_SAMPLING_RATE, + latencyHint: 'interactive' + }); - if (token.isCancellationRequested) { - return; + const microphoneSource = audioContext.createMediaStreamSource(microphoneDevice); + + token.onCancellationRequested(() => { + try { + for (const track of microphoneDevice.getTracks()) { + track.stop(); + } + + microphoneSource.disconnect(); + audioContext.close(); + } finally { + recordingDone.complete(); + } + }); + + await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js').toString(true)); + + if (token.isCancellationRequested) { + return; + } + + const voiceTranscriptionTarget = new VoiceTranscriptionWorkletNode(audioContext, { + channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, + channelCountMode: 'explicit' + }, onDidTranscribe, this.sharedProcessService); + await voiceTranscriptionTarget.start(token); + + if (token.isCancellationRequested) { + return; + } + + microphoneSource.connect(voiceTranscriptionTarget); + + progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); + recordingReady.complete(); + + return recordingDone.p; + } catch (error) { + this.notificationService.error(localize('voiceTranscriptionError', "Voice transcription failed: {0}", error.message)); + + recordingReady.error(error); + recordingDone.error(error); } - - const voiceTranscriptionTarget = new VoiceTranscriptionWorkletNode(audioContext, { - channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - channelCountMode: 'explicit' - }, onDidTranscribe, this.sharedProcessService); - await voiceTranscriptionTarget.start(token); - - if (token.isCancellationRequested) { - return; - } - - microphoneSource.connect(voiceTranscriptionTarget); - - progress.report({ message: localize('voiceTranscriptionRecording', "Recording from microphone...") }); - recordingReady.complete(); - - return recordingDone.p; }); return recordingReady.p; From 5fbf0d2ed173d2819a4c3653485e3f4f6945bd9b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 21 Aug 2023 17:42:44 +0200 Subject: [PATCH 056/607] passing in instead the diagnostic collection --- .../src/languageFeatures/diagnostics.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 0743d34f0e2..80093b4701c 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -159,7 +159,7 @@ class DiagnosticsTelemetryManager extends Disposable { constructor( private readonly _telemetryReporter: TelemetryReporter, - private readonly _getDiagnostics: (uri: vscode.Uri) => readonly vscode.Diagnostic[] + private readonly _diagnosticsCollection: vscode.DiagnosticCollection, ) { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { @@ -173,8 +173,7 @@ class DiagnosticsTelemetryManager extends Disposable { private _updateAllDiagnosticCodesAfterTimeout() { clearTimeout(this._timeout); - const uris = vscode.workspace.textDocuments.map(doc => doc.uri); - this._timeout = setTimeout(() => { uris.forEach((uri) => this._updateDiagnosticCodes(uri)); }, 5000); + this._timeout = setTimeout(() => this._updateDiagnosticCodes(), 5000); } private _increaseDiagnosticCodeCount(code: string | number | undefined) { @@ -184,15 +183,16 @@ class DiagnosticsTelemetryManager extends Disposable { this._diagnosticCodesMap.set(Number(code), (this._diagnosticCodesMap.get(Number(code)) || 0) + 1); } - private _updateDiagnosticCodes(uri: vscode.Uri) { - const uriString = uri.toString(); - const previousDiagnostics = this._diagnosticSnapshotsMap.get(uriString); - const currentDiagnostics = this._getDiagnostics(uri); - this._diagnosticSnapshotsMap.set(uriString, currentDiagnostics); - const diagnosticsDiff = currentDiagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => equals(diagnostic, previousDiagnostic))); - diagnosticsDiff.forEach((diagnostic) => { - const code = diagnostic.code; - this._increaseDiagnosticCodeCount(typeof code === 'string' || typeof code === 'number' ? code : code?.value); + private _updateDiagnosticCodes() { + this._diagnosticsCollection.forEach((uri, diagnostics) => { + const uriString = uri.toString(); + const previousDiagnostics = this._diagnosticSnapshotsMap.get(uriString); + this._diagnosticSnapshotsMap.set(uriString, diagnostics); + const diagnosticsDiff = diagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => equals(diagnostic, previousDiagnostic))); + diagnosticsDiff.forEach((diagnostic) => { + const code = diagnostic.code; + this._increaseDiagnosticCodeCount(typeof code === 'string' || typeof code === 'number' ? code : code?.value); + }); }); } @@ -242,7 +242,7 @@ export class DiagnosticsManager extends Disposable { this._currentDiagnostics = this._register(vscode.languages.createDiagnosticCollection(owner)); // Here we are selecting only 1 user out of 1000 to send telemetry diagnostics if (Math.random() * 1000 <= 1 || configuration.enableDiagnosticsTelemetry) { - this._register(new DiagnosticsTelemetryManager(telemetryReporter, this.getDiagnostics.bind(this))); + this._register(new DiagnosticsTelemetryManager(telemetryReporter, this._currentDiagnostics)); } } From 7a5894a5270b8131afd05efaee78160f346c2a63 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 21 Aug 2023 21:05:35 +0200 Subject: [PATCH 057/607] voice - actions tweaks --- src/vs/workbench/contrib/chat/browser/chat.ts | 3 +- .../contrib/chat/browser/chatQuick.ts | 11 ++++- .../actions/chatVoiceInputActions.ts | 47 ++++++++++++++----- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 7450fb52efa..2950dd6579b 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -35,7 +35,8 @@ export interface IChatWidgetService { export interface IQuickChatService { readonly _serviceBrand: undefined; - enabled: boolean; + readonly onDidClose: Event; + readonly enabled: boolean; toggle(providerId?: string, query?: string): void; focus(): void; open(): void; diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 20b6076bb0c..e75116f1551 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -5,6 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IContextKeyService, IScopedContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -17,9 +18,12 @@ import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; -export class QuickChatService implements IQuickChatService { +export class QuickChatService extends Disposable implements IQuickChatService { readonly _serviceBrand: undefined; + private readonly _onDidClose = this._register(new Emitter()); + readonly onDidClose = this._onDidClose.event; + private _input: IQuickWidget | undefined; private _currentChat: QuickChat | undefined; @@ -27,7 +31,9 @@ export class QuickChatService implements IQuickChatService { @IQuickInputService private readonly quickInputService: IQuickInputService, @IChatService private readonly chatService: IChatService, @IInstantiationService private readonly instantiationService: IInstantiationService, - ) { } + ) { + super(); + } get enabled(): boolean { return this.chatService.getProviderInfos().length > 0; @@ -84,6 +90,7 @@ export class QuickChatService implements IQuickChatService { disposableStore.add(this._input.onDidHide(() => { disposableStore.dispose(); this._input = undefined; + this._onDidClose.fire(); })); this._currentChat.focus(); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 9e3058c7405..2ebbbb79bf0 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -3,9 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from 'vs/base/common/event'; +import { firstOrDefault } from 'vs/base/common/arrays'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; @@ -15,6 +17,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; const CONTEXT_CHAT_VOICE_INPUT_GETTING_READY = new RawContextKey('chatVoiceInputGettingReady', false, { type: 'boolean', description: localize('chatVoiceInputGettingReady', "True when there is voice input for chat getting ready.") }); @@ -50,11 +53,14 @@ class ChatVoiceInputSession { @IWorkbenchVoiceRecognitionService private readonly voiceRecognitionService: IWorkbenchVoiceRecognitionService ) { } - async start(context: IChatVoiceInputActionContext): Promise { + async start(context: IChatVoiceInputActionContext, disposables?: IDisposable[]): Promise { this.stop(); this.chatVoiceInputGettingReadyKey.set(true); this.currentChatVoiceInputSession = new DisposableStore(); + for (const disposable of disposables ?? []) { + this.currentChatVoiceInputSession.add(disposable); + } const cts = new CancellationTokenSource(); this.currentChatVoiceInputSession.add(toDisposable(() => cts.dispose(true))); @@ -108,6 +114,7 @@ class ChatVoiceInputSession { } class StartChatVoiceInputAction extends Action2 { + static readonly ID = 'workbench.action.chat.startVoiceInput'; constructor() { @@ -118,6 +125,7 @@ class StartChatVoiceInputAction extends Action2 { original: 'Start Voice Input' }, category: CHAT_CATEGORY, + f1: true, icon: Codicon.record, precondition: CONTEXT_CHAT_VOICE_INPUT_GETTING_READY.negate(), menu: { @@ -129,23 +137,33 @@ class StartChatVoiceInputAction extends Action2 { }); } - run(accessor: ServicesAccessor, ...args: any[]): void { + async run(accessor: ServicesAccessor, ...args: any[]): Promise { const chatWidgetService = accessor.get(IChatWidgetService); + const chatService = accessor.get(IChatService); + const instantiationService = accessor.get(IInstantiationService); let context = args[0]; if (!isVoiceInputActionContext(context)) { if (chatWidgetService.lastFocusedWidget?.hasInputFocus()) { context = { widget: chatWidgetService.lastFocusedWidget }; } else { - return; + const provider = firstOrDefault(chatService.getProviderInfos()); + if (provider) { + context = { widget: await chatWidgetService.revealViewForProvider(provider.id) }; + } } } - ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).start(context); + if (!isVoiceInputActionContext(context)) { + return; + } + + ChatVoiceInputSession.getInstance(instantiationService).start(context); } } class StopChatVoiceInputAction extends Action2 { + static readonly ID = 'workbench.action.chat.stopVoiceInput'; constructor() { @@ -173,15 +191,16 @@ class StopChatVoiceInputAction extends Action2 { } } -class StartVoiceQuickChatAction extends Action2 { - static readonly ID = 'workbench.action.chat.startVoiceQuickChat'; +class VoiceQuickChatAction extends Action2 { + + static readonly ID = 'workbench.action.chat.voiceQuickChat'; constructor() { super({ - id: StartVoiceQuickChatAction.ID, + id: VoiceQuickChatAction.ID, title: { - value: localize('interactive.startVoiceChat.label', "Start Voice Quick Chat"), - original: 'Start Voice Quick Chat' + value: localize('interactive.voiceQuickChat.label', "Quick Chat with Voice Input"), + original: 'Quick Chat with Voice Input' }, category: CHAT_CATEGORY, f1: true @@ -191,12 +210,16 @@ class StartVoiceQuickChatAction extends Action2 { run(accessor: ServicesAccessor): void { const quickChatService = accessor.get(IQuickChatService); const chatWidgetService = accessor.get(IChatWidgetService); + const instantiationService = accessor.get(IInstantiationService); quickChatService.open(); + const disposables: IDisposable[] = []; + Event.once(quickChatService.onDidClose)(() => ChatVoiceInputSession.getInstance(instantiationService).stop(), undefined, disposables); + const widget = chatWidgetService.lastFocusedWidget; if (widget) { - ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).start({ widget }); + ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).start({ widget }, disposables); } } } @@ -204,5 +227,5 @@ class StartVoiceQuickChatAction extends Action2 { export function registerChatVoiceInputActions() { registerAction2(StartChatVoiceInputAction); registerAction2(StopChatVoiceInputAction); - registerAction2(StartVoiceQuickChatAction); + registerAction2(VoiceQuickChatAction); } From 41cb0fc5647a171b40a89c1e1ceb5e65913132ec Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 22 Aug 2023 06:45:00 +0200 Subject: [PATCH 058/607] voice - reduce audio buffer delay --- .../electron-sandbox/voiceTranscriptionWorklet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts index a298b23b29d..47c7baf3433 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts @@ -12,7 +12,7 @@ declare class AudioWorkletProcessor { class VoiceTranscriptionWorklet extends AudioWorkletProcessor { - private static readonly BUFFER_TIMESPAN = 2000; + private static readonly BUFFER_TIMESPAN = 1000; private startTime: number | undefined = undefined; private stopped: boolean = false; From 32c476a056ff665fa96d9325a01eb84323acb3cb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 09:31:25 +0200 Subject: [PATCH 059/607] adding a comment right above the setting --- .../src/configuration/configuration.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index 2650d015d88..23b1ee8b466 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -200,6 +200,7 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu } protected readEnableDiagnosticsTelemetry(configuration: vscode.WorkspaceConfiguration): boolean { + /** This setting does not appear in the settings view, as it is not to be enabled by users outside the team */ return configuration.get('typescript.enableDiagnosticsTelemetry', false); } From df5bf004f46fad28759643b047fdfd638058cba1 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 09:33:41 +0200 Subject: [PATCH 060/607] checking also that the file is of type typescriptreact --- .../src/languageFeatures/diagnostics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 80093b4701c..5dbdf3f350d 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -163,7 +163,7 @@ class DiagnosticsTelemetryManager extends Disposable { ) { super(); this._register(vscode.workspace.onDidChangeTextDocument(e => { - if (e.document.languageId === 'typescript') { + if (e.document.languageId === 'typescript' || e.document.languageId === 'typescriptreact') { this._updateAllDiagnosticCodesAfterTimeout(); } })); From 46ca479aa391b35b39cc11900f21f549dd94f3e9 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 09:42:12 +0200 Subject: [PATCH 061/607] using a resource map now, need to normalize to a string? --- .../src/languageFeatures/diagnostics.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index 5dbdf3f350d..e031a7b6061 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -154,7 +154,7 @@ class DiagnosticSettings { class DiagnosticsTelemetryManager extends Disposable { private readonly _diagnosticCodesMap = new Map(); - private readonly _diagnosticSnapshotsMap = new Map(); + private readonly _diagnosticSnapshotsMap = new ResourceMap(uri => uri.toString(), { onCaseInsensitiveFileSystem: false }); private _timeout: NodeJS.Timeout | undefined; constructor( @@ -185,9 +185,8 @@ class DiagnosticsTelemetryManager extends Disposable { private _updateDiagnosticCodes() { this._diagnosticsCollection.forEach((uri, diagnostics) => { - const uriString = uri.toString(); - const previousDiagnostics = this._diagnosticSnapshotsMap.get(uriString); - this._diagnosticSnapshotsMap.set(uriString, diagnostics); + const previousDiagnostics = this._diagnosticSnapshotsMap.get(uri); + this._diagnosticSnapshotsMap.set(uri, diagnostics); const diagnosticsDiff = diagnostics.filter((diagnostic) => !previousDiagnostics?.some((previousDiagnostic) => equals(diagnostic, previousDiagnostic))); diagnosticsDiff.forEach((diagnostic) => { const code = diagnostic.code; From 82cbf90600daea047c9153a638f01b42d670f939 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 09:43:11 +0200 Subject: [PATCH 062/607] clearing the timeout on dispose of the class --- .../src/languageFeatures/diagnostics.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index e031a7b6061..a2a889d1ba2 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -218,6 +218,11 @@ class DiagnosticsTelemetryManager extends Disposable { } }, 5 * 60 * 1000); // 5 minutes } + + override dispose() { + super.dispose(); + clearTimeout(this._timeout); + } } export class DiagnosticsManager extends Disposable { From 61f5cc38e3f3f16cacbd4e4f223e6f3c39824f14 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 11:14:57 +0200 Subject: [PATCH 063/607] removed the transition that was appearing in the code, now need to set the cursor so it always a pointer when clicking on the toggle unfold icon --- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 0bec6141d1f..12ad932df08 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -228,6 +228,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { // Added for the folding toggle icons innerLineNumberHTML.style.float = 'left'; + if (foldingModel) { const foldingRegions = foldingModel.regions; const indexOfLine = foldingRegions.findRange(line); @@ -243,7 +244,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } else { divToUnfold.className = ThemeIcon.asClassName(foldingExpandedIcon); } - divToUnfold.style.transition = 'opacity 250ms linear'; + + console.log('inside of render child node for line : ', line); + // divToUnfold.style.transition = 'opacity 250ms linear'; divToUnfold.style.opacity = '0'; divToUnfold.style.height = '0px'; divToUnfold.style.cursor = 'default'; @@ -274,6 +277,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { divToUnfold.style.cursor = 'pointer'; })); this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OUT, () => { + console.log('inside of mouse out for line : ', line); divToUnfold.style.transition = 'opacity 250ms linear'; divToUnfold.style.opacity = '0'; divToUnfold.style.height = '0px'; From cee1c706769c7c3b2c9714a19120f4933ffd725b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 11:23:38 +0200 Subject: [PATCH 064/607] now the cursor does not flicker anymore but I need to find a way to unset the cursor pointer after the complete rendering has been done, also why is every line rendering twice? --- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 12ad932df08..9413571b9db 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -168,6 +168,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { + const editorDomNode = this._editor.getDomNode(); const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); @@ -249,7 +250,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { // divToUnfold.style.transition = 'opacity 250ms linear'; divToUnfold.style.opacity = '0'; divToUnfold.style.height = '0px'; - divToUnfold.style.cursor = 'default'; + // divToUnfold.style.cursor = 'default'; divToUnfold.classList.add('unfold-icon'); lineNumberHTMLNode.append(divToUnfold); @@ -269,6 +270,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const newHeight = scrollTop; // - (collapsed ? 0 : 18); console.log('newHeight : ', newHeight); this._editor.setScrollTop(newHeight); + if (editorDomNode) { + editorDomNode.style.cursor = 'pointer'; + } + })); this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OVER, () => { divToUnfold.style.opacity = '1'; From e6e0b506d30d22bf8e8494e38788a42300a945c5 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 11:48:05 +0200 Subject: [PATCH 065/607] adding some notes --- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 9413571b9db..4d2d835b881 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -246,7 +246,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { divToUnfold.className = ThemeIcon.asClassName(foldingExpandedIcon); } - console.log('inside of render child node for line : ', line); // divToUnfold.style.transition = 'opacity 250ms linear'; divToUnfold.style.opacity = '0'; divToUnfold.style.height = '0px'; @@ -267,7 +266,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { // TODO: Likely a more complicated mathematical equation that involves finding the position given the new number of lines in the sticky widget // there appears to be an error here, doesn't behave exactly as expected // TODO: continuous rerendering of the arrow, which need not be rerendered if already in the right collapsed state - const newHeight = scrollTop; // - (collapsed ? 0 : 18); + console.log('collapsed : ', collapsed); + console.log('18 * (this._stickyLines.length - 1 - index) : ', 18 * (this._stickyLines.length - 1 - index)); + // When collapsed we should go to scrollTop - 18 * (this._stickyLines.length - index) + const newHeight = scrollTop - 18 * (this._stickyLines.length - index); console.log('newHeight : ', newHeight); this._editor.setScrollTop(newHeight); if (editorDomNode) { @@ -282,7 +284,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { divToUnfold.style.cursor = 'pointer'; })); this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OUT, () => { - console.log('inside of mouse out for line : ', line); divToUnfold.style.transition = 'opacity 250ms linear'; divToUnfold.style.opacity = '0'; divToUnfold.style.height = '0px'; From 191c3345f175d5fa40a6c8ce6fc9fabc4754b05b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 11:57:17 +0200 Subject: [PATCH 066/607] uncollapsing now works as expected, when it is uncollapsed, we jump to the correct part --- .../stickyScroll/browser/stickyScrollWidget.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index f6a8c8d1795..4a73254d35f 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -42,6 +42,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _stickyLines: RenderedStickyLine[] = []; private _lineNumbers: number[] = []; + private _startLineNumbers: number[] = []; + private _endLineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; private _minContentWidthInPx: number = 0; @@ -118,9 +120,13 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineNumbers[state.showEndForLine] = state.endLineNumbers[state.showEndForLine]; } this._lineNumbers = lineNumbers; + this._startLineNumbers = state.startLineNumbers; + this._endLineNumbers = state.endLineNumbers; } else { this._lastLineRelativePosition = 0; this._lineNumbers = []; + this._startLineNumbers = []; + this._endLineNumbers = []; } this._renderRootNode(); } @@ -273,9 +279,14 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { // there appears to be an error here, doesn't behave exactly as expected // TODO: continuous rerendering of the arrow, which need not be rerendered if already in the right collapsed state console.log('collapsed : ', collapsed); - console.log('18 * (this._stickyLines.length - 1 - index) : ', 18 * (this._stickyLines.length - 1 - index)); // When collapsed we should go to scrollTop - 18 * (this._stickyLines.length - index) - const newHeight = scrollTop - 18 * (this._stickyLines.length - index); + + const topOfStart = this._editor.getTopForLineNumber(this._startLineNumbers[index]) - lineHeight * (index) + 1; + const topOfEnd = this._editor.getTopForLineNumber(this._endLineNumbers[index]); + console.log('topOfStart : ', topOfStart); + console.log('topOfEnd : ', topOfEnd); + + const newHeight = collapsed ? topOfEnd : topOfStart; console.log('newHeight : ', newHeight); this._editor.setScrollTop(newHeight); if (editorDomNode) { From 8d84900bd7af1acaef15ed37858a1a44b076989f Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 11:59:35 +0200 Subject: [PATCH 067/607] the jumping works correctly and the correct top values for the scrolling are set --- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 4a73254d35f..c6f93056909 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -282,7 +282,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { // When collapsed we should go to scrollTop - 18 * (this._stickyLines.length - index) const topOfStart = this._editor.getTopForLineNumber(this._startLineNumbers[index]) - lineHeight * (index) + 1; - const topOfEnd = this._editor.getTopForLineNumber(this._endLineNumbers[index]); + const topOfEnd = this._editor.getTopForLineNumber(this._endLineNumbers[index]) - lineHeight * (index + 1) + 1; console.log('topOfStart : ', topOfStart); console.log('topOfEnd : ', topOfEnd); From 91c10f647b848455399f2e956340d107ed818874 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 12:03:05 +0200 Subject: [PATCH 068/607] removing some of the comments --- .../stickyScroll/browser/stickyScrollWidget.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index c6f93056909..8dbe488a8e3 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -258,10 +258,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { divToUnfold.className = ThemeIcon.asClassName(foldingExpandedIcon); } - // divToUnfold.style.transition = 'opacity 250ms linear'; divToUnfold.style.opacity = '0'; divToUnfold.style.height = '0px'; - // divToUnfold.style.cursor = 'default'; divToUnfold.classList.add('unfold-icon'); lineNumberHTMLNode.append(divToUnfold); @@ -269,25 +267,11 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { let collapsed = isCollapsed; this._disposableStore.add(dom.addDisposableListener(divToUnfold, dom.EventType.CLICK, () => { - console.log('line : ', line); - - const scrollTop = this._editor.getTopForLineNumber(line) + 1; - console.log('scrollTop : ', scrollTop); toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); collapsed = !collapsed; - // TODO: Likely a more complicated mathematical equation that involves finding the position given the new number of lines in the sticky widget - // there appears to be an error here, doesn't behave exactly as expected - // TODO: continuous rerendering of the arrow, which need not be rerendered if already in the right collapsed state - console.log('collapsed : ', collapsed); - // When collapsed we should go to scrollTop - 18 * (this._stickyLines.length - index) - const topOfStart = this._editor.getTopForLineNumber(this._startLineNumbers[index]) - lineHeight * (index) + 1; const topOfEnd = this._editor.getTopForLineNumber(this._endLineNumbers[index]) - lineHeight * (index + 1) + 1; - console.log('topOfStart : ', topOfStart); - console.log('topOfEnd : ', topOfEnd); - const newHeight = collapsed ? topOfEnd : topOfStart; - console.log('newHeight : ', newHeight); this._editor.setScrollTop(newHeight); if (editorDomNode) { editorDomNode.style.cursor = 'pointer'; From dd586ff24943a4ae982dea1dac0de9758ff69a35 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 12:35:17 +0200 Subject: [PATCH 069/607] recording the line numbers of interest in a variable before the toggling happens --- .../stickyScroll/browser/stickyScrollWidget.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 8dbe488a8e3..70ef404f673 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -239,6 +239,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } lineNumberHTMLNode.appendChild(innerLineNumberHTML); + const lineNumbersWidth = minimapSide === 'left' ? layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth : layoutInfo.contentLeft; + lineNumberHTMLNode.style.width = `${lineNumbersWidth}px`; + // Added for the folding toggle icons innerLineNumberHTML.style.float = 'left'; @@ -267,16 +270,17 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { let collapsed = isCollapsed; this._disposableStore.add(dom.addDisposableListener(divToUnfold, dom.EventType.CLICK, () => { - toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); collapsed = !collapsed; - const topOfStart = this._editor.getTopForLineNumber(this._startLineNumbers[index]) - lineHeight * (index) + 1; - const topOfEnd = this._editor.getTopForLineNumber(this._endLineNumbers[index]) - lineHeight * (index + 1) + 1; + const startLineToCalculate = this._startLineNumbers[index]; + const endLineToCalculate = this._endLineNumbers[index]; + toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); + const topOfStart = this._editor.getTopForLineNumber(startLineToCalculate) - lineHeight * (index) + 1; + const topOfEnd = this._editor.getTopForLineNumber(endLineToCalculate) - lineHeight * (index + 1) + 1; const newHeight = collapsed ? topOfEnd : topOfStart; this._editor.setScrollTop(newHeight); if (editorDomNode) { editorDomNode.style.cursor = 'pointer'; } - })); this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OVER, () => { divToUnfold.style.opacity = '1'; From 225721e5277c692bcbec4e06084596a88ac4baf6 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 12:56:25 +0200 Subject: [PATCH 070/607] cleaning the code --- .../stickyScroll/browser/stickyScrollWidget.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 70ef404f673..a958f90f340 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -42,8 +42,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _stickyLines: RenderedStickyLine[] = []; private _lineNumbers: number[] = []; - private _startLineNumbers: number[] = []; - private _endLineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; private _minContentWidthInPx: number = 0; @@ -120,13 +118,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineNumbers[state.showEndForLine] = state.endLineNumbers[state.showEndForLine]; } this._lineNumbers = lineNumbers; - this._startLineNumbers = state.startLineNumbers; - this._endLineNumbers = state.endLineNumbers; } else { this._lastLineRelativePosition = 0; this._lineNumbers = []; - this._startLineNumbers = []; - this._endLineNumbers = []; } this._renderRootNode(); } @@ -250,6 +244,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const indexOfLine = foldingRegions.findRange(line); const isCollapsed = foldingRegions.isCollapsed(indexOfLine); const startLineNumber = foldingRegions.getStartLineNumber(indexOfLine); + const endLineNumber = foldingRegions.getEndLineNumber(indexOfLine); const isFoldingLine = line === startLineNumber; if (isFoldingLine) { @@ -271,11 +266,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._disposableStore.add(dom.addDisposableListener(divToUnfold, dom.EventType.CLICK, () => { collapsed = !collapsed; - const startLineToCalculate = this._startLineNumbers[index]; - const endLineToCalculate = this._endLineNumbers[index]; toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); - const topOfStart = this._editor.getTopForLineNumber(startLineToCalculate) - lineHeight * (index) + 1; - const topOfEnd = this._editor.getTopForLineNumber(endLineToCalculate) - lineHeight * (index + 1) + 1; + + const topOfStart = this._editor.getTopForLineNumber(startLineNumber) - lineHeight * (index) + 1; + const topOfEnd = this._editor.getTopForLineNumber(endLineNumber) - lineHeight * (index) + 1; const newHeight = collapsed ? topOfEnd : topOfStart; this._editor.setScrollTop(newHeight); if (editorDomNode) { From e7cd474d16f2c5902834c2b0e0dddf0aaac685a7 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 14:23:19 +0200 Subject: [PATCH 071/607] cleaning the code --- .../browser/stickyScrollWidget.ts | 125 +++++++++--------- 1 file changed, 61 insertions(+), 64 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index a958f90f340..389df12346d 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -139,15 +139,11 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!this._editor._getViewModel()) { return; } - - // Folding let foldingModel: FoldingModel | null = null; const foldingController = FoldingController.get(this._editor); if (foldingController) { foldingModel = await foldingController.getFoldingModel(); } - // Folding - const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { const { lineNumberHTMLNode, renderedStickyLine } = this._renderChildNode(index, line, layoutInfo, foldingModel); @@ -174,7 +170,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { - const editorDomNode = this._editor.getDomNode(); const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); @@ -216,6 +211,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const lineNumberHTMLNode = document.createElement('span'); lineNumberHTMLNode.className = 'sticky-line-number'; lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`; + const lineNumbersWidth = minimapSide === 'left' ? layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth : layoutInfo.contentLeft; + lineNumberHTMLNode.style.width = `${lineNumbersWidth}px`; const innerLineNumberHTML = document.createElement('span'); if (lineNumberOption.renderType === RenderLineNumbersType.On || lineNumberOption.renderType === RenderLineNumbersType.Interval && line % 10 === 0) { @@ -226,6 +223,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { innerLineNumberHTML.className = 'sticky-line-number-inner'; innerLineNumberHTML.style.lineHeight = `${lineHeight}px`; innerLineNumberHTML.style.width = `${layoutInfo.lineNumbersWidth}px`; + innerLineNumberHTML.style.float = 'left'; if (minimapSide === 'left') { innerLineNumberHTML.style.paddingLeft = `${layoutInfo.lineNumbersLeft - layoutInfo.minimap.minimapCanvasOuterWidth}px`; } else if (minimapSide === 'right') { @@ -233,65 +231,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } lineNumberHTMLNode.appendChild(innerLineNumberHTML); - const lineNumbersWidth = minimapSide === 'left' ? layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth : layoutInfo.contentLeft; - lineNumberHTMLNode.style.width = `${lineNumbersWidth}px`; - - // Added for the folding toggle icons - innerLineNumberHTML.style.float = 'left'; - - if (foldingModel) { - const foldingRegions = foldingModel.regions; - const indexOfLine = foldingRegions.findRange(line); - const isCollapsed = foldingRegions.isCollapsed(indexOfLine); - const startLineNumber = foldingRegions.getStartLineNumber(indexOfLine); - const endLineNumber = foldingRegions.getEndLineNumber(indexOfLine); - const isFoldingLine = line === startLineNumber; - - if (isFoldingLine) { - const divToUnfold = document.createElement('div'); - divToUnfold.style.float = 'right'; - if (isCollapsed) { - divToUnfold.className = ThemeIcon.asClassName(foldingCollapsedIcon); - } else { - divToUnfold.className = ThemeIcon.asClassName(foldingExpandedIcon); - } - - divToUnfold.style.opacity = '0'; - divToUnfold.style.height = '0px'; - - divToUnfold.classList.add('unfold-icon'); - lineNumberHTMLNode.append(divToUnfold); - - let collapsed = isCollapsed; - - this._disposableStore.add(dom.addDisposableListener(divToUnfold, dom.EventType.CLICK, () => { - collapsed = !collapsed; - toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); - - const topOfStart = this._editor.getTopForLineNumber(startLineNumber) - lineHeight * (index) + 1; - const topOfEnd = this._editor.getTopForLineNumber(endLineNumber) - lineHeight * (index) + 1; - const newHeight = collapsed ? topOfEnd : topOfStart; - this._editor.setScrollTop(newHeight); - if (editorDomNode) { - editorDomNode.style.cursor = 'pointer'; - } - })); - this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OVER, () => { - divToUnfold.style.opacity = '1'; - divToUnfold.style.height = '18px'; - divToUnfold.style.width = '18px'; - divToUnfold.style.cursor = 'pointer'; - })); - this._disposableStore.add(dom.addDisposableListener(lineNumberHTMLNode, dom.EventType.MOUSE_OUT, () => { - divToUnfold.style.transition = 'opacity 250ms linear'; - divToUnfold.style.opacity = '0'; - divToUnfold.style.height = '0px'; - divToUnfold.style.cursor = 'default'; - })); - } - } - // Added with the folding toggle icons - this._editor.applyFontInfo(lineHTMLNode); this._editor.applyFontInfo(innerLineNumberHTML); @@ -317,12 +256,70 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; + this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); + return { lineNumberHTMLNode, renderedStickyLine: new RenderedStickyLine(line, lineHTMLNode, renderOutput.characterMapping) }; } + private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null, index: number, line: number): void { + if (!foldingModel) { + return; + } + const foldingRegions = foldingModel.regions; + const indexOfFoldingRegion = foldingRegions.findRange(line); + const startLineNumber = foldingRegions.getStartLineNumber(indexOfFoldingRegion); + const endLineNumber = foldingRegions.getEndLineNumber(indexOfFoldingRegion); + const isFoldingScope = line === startLineNumber; + if (!isFoldingScope) { + return; + } + + const foldingIcon = document.createElement('div'); + container.append(foldingIcon); + foldingIcon.classList.add('unfold-icon'); + foldingIcon.style.float = 'right'; + const isRegionCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); + if (isRegionCollapsed) { + foldingIcon.className = ThemeIcon.asClassName(foldingCollapsedIcon); + } else { + foldingIcon.className = ThemeIcon.asClassName(foldingExpandedIcon); + } + + foldingIcon.style.opacity = '0'; + foldingIcon.style.height = '0px'; + + this._disposableStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { + toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); + + const lineHeight = this._editor.getOption(EditorOption.lineHeight); + const topOfStart = this._editor.getTopForLineNumber(startLineNumber) - lineHeight * (index) + 1; + const topOfEnd = this._editor.getTopForLineNumber(endLineNumber) - lineHeight * (index) + 1; + const newHeight = isRegionCollapsed ? topOfStart : topOfEnd; + this._editor.setScrollTop(newHeight); + const editorDomNode = this._editor.getDomNode(); + if (editorDomNode) { + editorDomNode.style.cursor = 'pointer'; + } + })); + this._disposableStore.add(dom.addDisposableListener(container, dom.EventType.MOUSE_OVER, () => { + console.log('inside of mouse over'); + foldingIcon.style.opacity = '1'; + foldingIcon.style.height = '18px'; + foldingIcon.style.width = '18px'; + foldingIcon.style.cursor = 'pointer'; + })); + this._disposableStore.add(dom.addDisposableListener(container, dom.EventType.MOUSE_OUT, () => { + console.log('inside of mouse out'); + foldingIcon.style.transition = 'opacity 250ms linear'; + foldingIcon.style.opacity = '0'; + foldingIcon.style.height = '0px'; + foldingIcon.style.cursor = 'default'; + })); + } + private _updateMinContentWidth() { this._minContentWidthInPx = 0; for (const stickyLine of this._stickyLines) { From dc2c952bf55b0ddcd3e9bc1b134292a5ca59892f Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 14:37:56 +0200 Subject: [PATCH 072/607] cleaning the code --- .../stickyScroll/browser/stickyScroll.css | 14 ++++++++ .../browser/stickyScrollWidget.ts | 35 ++++--------------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 8b1c053ad78..9de5247eaea 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -33,6 +33,20 @@ background-color: inherit; } +.monaco-editor .sticky-line-number .folding-icon { + width: 18px; + height: 18px; + float: right; + opacity: 0; + cursor: default; +} + +.monaco-editor .sticky-line-number .folding-icon:hover { + opacity: 1; + transition : opacity 250ms linear; + cursor: pointer; +} + .monaco-editor .sticky-line-content { width: var(--vscode-editorStickyScroll-scrollableWidth); background-color: inherit; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 389df12346d..f50b25e1059 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -34,7 +34,7 @@ const STICKY_LINE_INDEX_ATTR = 'data-sticky-line-index'; export class StickyScrollWidget extends Disposable implements IOverlayWidget { - private readonly _disposableStore = new DisposableStore(); + private readonly _foldingIconStore = new DisposableStore(); private readonly _rootDomNode: HTMLElement = document.createElement('div'); private readonly _lineNumbersDomNode: HTMLElement = document.createElement('div'); private readonly _linesDomNodeScrollable: HTMLElement = document.createElement('div'); @@ -265,6 +265,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null, index: number, line: number): void { + this._foldingIconStore.dispose(); if (!foldingModel) { return; } @@ -276,48 +277,26 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!isFoldingScope) { return; } - const foldingIcon = document.createElement('div'); container.append(foldingIcon); - foldingIcon.classList.add('unfold-icon'); - foldingIcon.style.float = 'right'; const isRegionCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); if (isRegionCollapsed) { foldingIcon.className = ThemeIcon.asClassName(foldingCollapsedIcon); } else { foldingIcon.className = ThemeIcon.asClassName(foldingExpandedIcon); } - - foldingIcon.style.opacity = '0'; - foldingIcon.style.height = '0px'; - - this._disposableStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { + foldingIcon.classList.add('folding-icon'); + this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); - const lineHeight = this._editor.getOption(EditorOption.lineHeight); - const topOfStart = this._editor.getTopForLineNumber(startLineNumber) - lineHeight * (index) + 1; - const topOfEnd = this._editor.getTopForLineNumber(endLineNumber) - lineHeight * (index) + 1; - const newHeight = isRegionCollapsed ? topOfStart : topOfEnd; - this._editor.setScrollTop(newHeight); + const topOfStartLine = this._editor.getTopForLineNumber(startLineNumber) - lineHeight * index + 1; + const topOfEndLine = this._editor.getTopForLineNumber(endLineNumber) - lineHeight * index + 1; + this._editor.setScrollTop(isRegionCollapsed ? topOfStartLine : topOfEndLine); const editorDomNode = this._editor.getDomNode(); if (editorDomNode) { editorDomNode.style.cursor = 'pointer'; } })); - this._disposableStore.add(dom.addDisposableListener(container, dom.EventType.MOUSE_OVER, () => { - console.log('inside of mouse over'); - foldingIcon.style.opacity = '1'; - foldingIcon.style.height = '18px'; - foldingIcon.style.width = '18px'; - foldingIcon.style.cursor = 'pointer'; - })); - this._disposableStore.add(dom.addDisposableListener(container, dom.EventType.MOUSE_OUT, () => { - console.log('inside of mouse out'); - foldingIcon.style.transition = 'opacity 250ms linear'; - foldingIcon.style.opacity = '0'; - foldingIcon.style.height = '0px'; - foldingIcon.style.cursor = 'default'; - })); } private _updateMinContentWidth() { From 60945f75f625ef2a8cd26926ee70102f2b84d6d6 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 14:42:34 +0200 Subject: [PATCH 073/607] simplifying the code --- .../stickyScroll/browser/stickyScrollWidget.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index f50b25e1059..6361f7bc7ce 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -139,11 +139,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!this._editor._getViewModel()) { return; } - let foldingModel: FoldingModel | null = null; - const foldingController = FoldingController.get(this._editor); - if (foldingController) { - foldingModel = await foldingController.getFoldingModel(); - } + this._foldingIconStore.dispose(); + const foldingModel: FoldingModel | null | undefined = await FoldingController.get(this._editor)?.getFoldingModel(); const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { const { lineNumberHTMLNode, renderedStickyLine } = this._renderChildNode(index, line, layoutInfo, foldingModel); @@ -169,7 +166,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._editor.layoutOverlayWidget(this); } - private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { + private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null | undefined): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); @@ -264,8 +261,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { }; } - private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null, index: number, line: number): void { - this._foldingIconStore.dispose(); + private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): void { if (!foldingModel) { return; } From 04d4f40b90a5eaeef2ca6ac5c89eea0ddca85b4f Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 14:44:27 +0200 Subject: [PATCH 074/607] resetting the controller to what it was --- .../contrib/stickyScroll/browser/stickyScrollController.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index e1e6bcf8f42..37647eccdf6 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -340,11 +340,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib sessionStore.clear(); })); this._register(gesture.onExecute(async e => { - if ( - e.target.type !== MouseTargetType.OVERLAY_WIDGET - || e.target.detail !== this._stickyScrollWidget.getId() - || e.target.element?.classList.contains('unfold-icon') - ) { + if (e.target.type !== MouseTargetType.OVERLAY_WIDGET || e.target.detail !== this._stickyScrollWidget.getId()) { // not hovering over our widget return; } From 85d79203825a0272d926fb1b8f3b0f251649b89f Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 14:52:06 +0200 Subject: [PATCH 075/607] simplfifying --- .../contrib/stickyScroll/browser/stickyScroll.css | 4 ++-- .../stickyScroll/browser/stickyScrollWidget.ts | 12 +++--------- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 9de5247eaea..956d5aff851 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -33,7 +33,7 @@ background-color: inherit; } -.monaco-editor .sticky-line-number .folding-icon { +.monaco-editor .sticky-line-number .codicon { width: 18px; height: 18px; float: right; @@ -41,7 +41,7 @@ cursor: default; } -.monaco-editor .sticky-line-number .folding-icon:hover { +.monaco-editor .sticky-line-number .codicon:hover { opacity: 1; transition : opacity 250ms linear; cursor: pointer; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 6361f7bc7ce..2d31af91c8d 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -140,7 +140,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return; } this._foldingIconStore.dispose(); - const foldingModel: FoldingModel | null | undefined = await FoldingController.get(this._editor)?.getFoldingModel(); + const foldingModel = await FoldingController.get(this._editor)?.getFoldingModel(); const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { const { lineNumberHTMLNode, renderedStickyLine } = this._renderChildNode(index, line, layoutInfo, foldingModel); @@ -273,15 +273,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!isFoldingScope) { return; } - const foldingIcon = document.createElement('div'); - container.append(foldingIcon); + const foldingIcon = container.appendChild(document.createElement('div')); const isRegionCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); - if (isRegionCollapsed) { - foldingIcon.className = ThemeIcon.asClassName(foldingCollapsedIcon); - } else { - foldingIcon.className = ThemeIcon.asClassName(foldingExpandedIcon); - } - foldingIcon.classList.add('folding-icon'); + foldingIcon.className = ThemeIcon.asClassName(isRegionCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); const lineHeight = this._editor.getOption(EditorOption.lineHeight); From d8d115bf7f7ed04f0a533ca087d0b12fcc9cdcdc Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 22 Aug 2023 15:32:59 +0200 Subject: [PATCH 076/607] remove the transition because causes bugs --- src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css | 1 - .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 956d5aff851..ff9afd45bd0 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -43,7 +43,6 @@ .monaco-editor .sticky-line-number .codicon:hover { opacity: 1; - transition : opacity 250ms linear; cursor: pointer; } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 2d31af91c8d..449df81753f 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -282,10 +282,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const topOfStartLine = this._editor.getTopForLineNumber(startLineNumber) - lineHeight * index + 1; const topOfEndLine = this._editor.getTopForLineNumber(endLineNumber) - lineHeight * index + 1; this._editor.setScrollTop(isRegionCollapsed ? topOfStartLine : topOfEndLine); - const editorDomNode = this._editor.getDomNode(); - if (editorDomNode) { - editorDomNode.style.cursor = 'pointer'; - } })); } From b1d5542cfd04b251f3e26679c0954d45667034e7 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 22 Aug 2023 20:22:32 +0000 Subject: [PATCH 077/607] Finalize proposed env workspace collection API --- extensions/vscode-api-tests/package.json | 1 - .../src/singlefolder-tests/terminal.test.ts | 5 +-- .../api/common/extHostTerminalService.ts | 4 -- .../common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 31 ++++++++++++-- ...scode.proposed.envCollectionWorkspace.d.ts | 42 ------------------- 6 files changed, 30 insertions(+), 54 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 5ee95741ff0..911f7b746ac 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -51,7 +51,6 @@ "telemetry", "windowActivity", "interactiveUserActions", - "envCollectionWorkspace", "envCollectionOptions" ], "private": true, diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 76408d2de77..4275898e244 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { deepStrictEqual, doesNotThrow, equal, ok, strictEqual, throws } from 'assert'; -import { commands, ConfigurationTarget, Disposable, env, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, GlobalEnvironmentVariableCollection, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode'; +import { commands, ConfigurationTarget, Disposable, env, EnvironmentVariableMutator, EnvironmentVariableMutatorOptions, EnvironmentVariableMutatorType, EventEmitter, ExtensionContext, extensions, ExtensionTerminalOptions, Pseudoterminal, Terminal, TerminalDimensions, TerminalExitReason, TerminalOptions, TerminalState, UIKind, Uri, window, workspace } from 'vscode'; import { assertNoRpc, poll } from '../utils'; // Disable terminal tests: @@ -912,8 +912,7 @@ import { assertNoRpc, poll } from '../utils'; }); test('get and forEach should work (scope)', () => { - // TODO: Remove cast once `envCollectionWorkspace` API is finalized. - const collection = extensionContext.environmentVariableCollection as GlobalEnvironmentVariableCollection; + const collection = extensionContext.environmentVariableCollection; disposables.push({ dispose: () => collection.clear() }); const scope = { workspaceFolder: { uri: Uri.file('workspace1'), name: 'workspace1', index: 0 } }; const scopedCollection = collection.getScoped(scope); diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index c7d8b2a83d0..84f2cac70c1 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -929,10 +929,6 @@ class UnifiedEnvironmentVariableCollection { } getScopedEnvironmentVariableCollection(scope: vscode.EnvironmentVariableScope | undefined): IEnvironmentVariableCollection { - if (this._extension && scope) { - // TODO: This should be removed when the env var extension API(s) are stabilized - checkProposedApiEnabled(this._extension, 'envCollectionWorkspace'); - } const scopedCollectionKey = this.getScopeKey(scope); let scopedCollection = this.scopedCollections.get(scopedCollectionKey); if (!scopedCollection) { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 8b1cba0367d..336fc0caf50 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -41,7 +41,6 @@ export const allApiProposals = Object.freeze({ editSessionIdentityProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editSessionIdentityProvider.d.ts', editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts', envCollectionOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts', - envCollectionWorkspace: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts', envShellEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envShellEvent.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.d.ts b/src/vscode-dts/vscode.d.ts index db7d29f3b1b..8821ce6086c 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -7185,10 +7185,10 @@ declare module 'vscode' { readonly extensionPath: string; /** - * Gets the extension's environment variable collection for this workspace, enabling changes - * to be applied to terminal environment variables. + * Gets the extension's global environment variable collection for this workspace, enabling changes to be + * applied to terminal environment variables. */ - readonly environmentVariableCollection: EnvironmentVariableCollection; + readonly environmentVariableCollection: GlobalEnvironmentVariableCollection; /** * Get the absolute path of a resource contained in the extension. @@ -11429,6 +11429,31 @@ declare module 'vscode' { clear(): void; } + export interface GlobalEnvironmentVariableCollection extends EnvironmentVariableCollection { + /** + * Gets scope-specific environment variable collection for the extension. This enables alterations to + * terminal environment variables solely within the designated scope, and is applied in addition to (and + * after) the global collection. + * + * Each object obtained through this method is isolated and does not impact objects for other scopes, + * including the global collection. + * + * @param scope The scope to which the environment variable collection applies to. + * + * If a scope parameter is omitted, collection applicable to all relevant scopes for that parameter is + * returned. For instance, if the 'workspaceFolder' parameter is not specified, the collection that applies + * across all workspace folders will be returned. + */ + getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; + } + + export type EnvironmentVariableScope = { + /** + * Any specific workspace folder to get collection for. If unspecified, collection applicable to all workspace folders is returned. + */ + workspaceFolder?: WorkspaceFolder; + }; + /** * A location in the editor at which progress information can be shown. It depends on the * location how progress is visually represented. diff --git a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts b/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts deleted file mode 100644 index fb4b78e172b..00000000000 --- a/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts +++ /dev/null @@ -1,42 +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/171173 - - // export interface ExtensionContext { - // /** - // * Gets the extension's global environment variable collection for this workspace, enabling changes to be - // * applied to terminal environment variables. - // */ - // readonly environmentVariableCollection: GlobalEnvironmentVariableCollection; - // } - - export interface GlobalEnvironmentVariableCollection extends EnvironmentVariableCollection { - /** - * Gets scope-specific environment variable collection for the extension. This enables alterations to - * terminal environment variables solely within the designated scope, and is applied in addition to (and - * after) the global collection. - * - * Each object obtained through this method is isolated and does not impact objects for other scopes, - * including the global collection. - * - * @param scope The scope to which the environment variable collection applies to. - * - * If a scope parameter is omitted, collection applicable to all relevant scopes for that parameter is - * returned. For instance, if the 'workspaceFolder' parameter is not specified, the collection that applies - * across all workspace folders will be returned. - */ - getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; - } - - export type EnvironmentVariableScope = { - /** - * Any specific workspace folder to get collection for. If unspecified, collection applicable to all workspace folders is returned. - */ - workspaceFolder?: WorkspaceFolder; - }; -} From 252a9df9b5a90aaf7b7a58deb458fece45c083d1 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 22 Aug 2023 21:08:09 +0000 Subject: [PATCH 078/607] Remove unnecessay doc --- src/vscode-dts/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 8821ce6086c..aafc71211e2 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11449,7 +11449,7 @@ declare module 'vscode' { export type EnvironmentVariableScope = { /** - * Any specific workspace folder to get collection for. If unspecified, collection applicable to all workspace folders is returned. + * Any specific workspace folder to get collection for. */ workspaceFolder?: WorkspaceFolder; }; From 751a8128bd19c83e67e12c4a1f4c9918d6a3bc5b Mon Sep 17 00:00:00 2001 From: Joe Green Date: Tue, 22 Aug 2023 23:28:36 +0100 Subject: [PATCH 079/607] Fix scroll to top button colour consistency --- src/vs/workbench/contrib/extensions/browser/extensionEditor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 366c4d28382..0eb852277ab 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -803,7 +803,7 @@ export class ExtensionEditor extends EditorPane { #scroll-to-top span.icon::before { content: ""; display: block; - background: var(--vscode-button-foreground); + background: var(--vscode-button-secondaryForeground); /* Chevron up icon */ webkit-mask-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxNiAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTYgMTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRkZGRkZGO30KCS5zdDF7ZmlsbDpub25lO30KPC9zdHlsZT4KPHRpdGxlPnVwY2hldnJvbjwvdGl0bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04LDUuMWwtNy4zLDcuM0wwLDExLjZsOC04bDgsOGwtMC43LDAuN0w4LDUuMXoiLz4KPHJlY3QgY2xhc3M9InN0MSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+Cjwvc3ZnPgo='); -webkit-mask-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjIuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjAgMCAxNiAxNiIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMTYgMTY7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojRkZGRkZGO30KCS5zdDF7ZmlsbDpub25lO30KPC9zdHlsZT4KPHRpdGxlPnVwY2hldnJvbjwvdGl0bGU+CjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik04LDUuMWwtNy4zLDcuM0wwLDExLjZsOC04bDgsOGwtMC43LDAuN0w4LDUuMXoiLz4KPHJlY3QgY2xhc3M9InN0MSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE2Ii8+Cjwvc3ZnPgo='); From 8e524deece95d3aa772af5ce974e8a3a4aca422f Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 23 Aug 2023 14:57:53 +0200 Subject: [PATCH 080/607] not rendering when model is too large for tokenization --- .../contrib/stickyScroll/browser/stickyScrollController.ts | 5 ++--- .../contrib/codeEditor/browser/largeFileOptimizations.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 37647eccdf6..5764622526d 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -370,7 +370,6 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _readConfiguration() { const options = this._editor.getOption(EditorOption.stickyScroll); - if (options.enabled === false) { this._editor.removeOverlayWidget(this._stickyScrollWidget); this._sessionStore.clear(); @@ -429,10 +428,10 @@ export class StickyScrollController extends Disposable implements IEditorContrib } private _renderStickyScroll() { - if (!(this._editor.hasModel())) { + const model = this._editor.getModel(); + if (!model || model.isTooLargeForTokenization()) { return; } - const model = this._editor.getModel(); const stickyLineVersion = this._stickyLineCandidateProvider.getVersionId(); if (stickyLineVersion === undefined || stickyLineVersion === model.getVersionId()) { this._widgetState = this.findScrollWidgetState(); diff --git a/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts b/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts index 80e766a2a4f..52ac3a4afb1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/largeFileOptimizations.ts @@ -44,7 +44,7 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC 'Variable 0 will be a file name.' ] }, - "{0}: tokenization, wrapping and folding have been turned off for this large file in order to reduce memory usage and avoid freezing or crashing.", + "{0}: tokenization, wrapping, folding and sticky scroll have been turned off for this large file in order to reduce memory usage and avoid freezing or crashing.", path.basename(model.uri.path) ); From 1461c7d38225e5450d3d059b7872593a99d44df3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 23 Aug 2023 07:54:24 -0700 Subject: [PATCH 081/607] Finalize EnvironmentVariableMutatorOptions API Fixes #179476 --- extensions/vscode-api-tests/package.json | 3 +- .../api/common/extHostTerminalService.ts | 9 --- .../common/extensionsApiProposals.ts | 1 - src/vscode-dts/vscode.d.ts | 33 ++++++++++- .../vscode.proposed.envCollectionOptions.d.ts | 55 ------------------- 5 files changed, 31 insertions(+), 70 deletions(-) delete mode 100644 src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 9d50e393547..ac6fb719a90 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -52,8 +52,7 @@ "telemetry", "windowActivity", "interactiveUserActions", - "envCollectionWorkspace", - "envCollectionOptions" + "envCollectionWorkspace" ], "private": true, "activationEvents": [], diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index c7d8b2a83d0..c901a5cae3d 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -944,23 +944,14 @@ class UnifiedEnvironmentVariableCollection { } replace(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void { - if (this._extension && options) { - checkProposedApiEnabled(this._extension, 'envCollectionOptions'); - } this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace, options: options ?? { applyAtProcessCreation: true }, scope }); } append(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void { - if (this._extension && options) { - checkProposedApiEnabled(this._extension, 'envCollectionOptions'); - } this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append, options: options ?? { applyAtProcessCreation: true }, scope }); } prepend(variable: string, value: string, options: vscode.EnvironmentVariableMutatorOptions | undefined, scope: vscode.EnvironmentVariableScope | undefined): void { - if (this._extension && options) { - checkProposedApiEnabled(this._extension, 'envCollectionOptions'); - } this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend, options: options ?? { applyAtProcessCreation: true }, scope }); } diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 03d80474dad..be411915ec1 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -40,7 +40,6 @@ export const allApiProposals = Object.freeze({ dropMetadata: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.dropMetadata.d.ts', editSessionIdentityProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editSessionIdentityProvider.d.ts', editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts', - envCollectionOptions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts', envCollectionWorkspace: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envCollectionWorkspace.d.ts', envShellEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envShellEvent.d.ts', extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts', diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index db7d29f3b1b..6c962d2e0fd 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11334,6 +11334,22 @@ declare module 'vscode' { Prepend = 3 } + /** + * Options applied to the mutator. + */ + export interface EnvironmentVariableMutatorOptions { + /** + * Apply to the environment just before the process is created. + */ + applyAtProcessCreation?: boolean; + + /** + * Apply to the environment in the shell integration script. Note that this _will not_ apply + * the mutator if shell integration is disabled or not working for some reason. + */ + applyAtShellIntegration?: boolean; + } + /** * A type of mutation and its value to be applied to an environment variable. */ @@ -11347,6 +11363,11 @@ declare module 'vscode' { * The value to use for the variable. */ readonly value: string; + + /** + * Options applied to the mutator. + */ + readonly options: EnvironmentVariableMutatorOptions; } /** @@ -11376,8 +11397,10 @@ declare module 'vscode' { * * @param variable The variable to replace. * @param value The value to replace the variable with. + * @param options Options applied to the mutator, when no options are provided this will + * default to `{ applyAtProcessCreation: true }`. */ - replace(variable: string, value: string): void; + replace(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; /** * Append a value to an environment variable. @@ -11387,8 +11410,10 @@ declare module 'vscode' { * * @param variable The variable to append to. * @param value The value to append to the variable. + * @param options Options applied to the mutator, when no options are provided this will + * default to `{ applyAtProcessCreation: true }`. */ - append(variable: string, value: string): void; + append(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; /** * Prepend a value to an environment variable. @@ -11398,8 +11423,10 @@ declare module 'vscode' { * * @param variable The variable to prepend. * @param value The value to prepend to the variable. + * @param options Options applied to the mutator, when no options are provided this will + * default to `{ applyAtProcessCreation: true }`. */ - prepend(variable: string, value: string): void; + prepend(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; /** * Gets the mutator that this collection applies to a variable, if any. diff --git a/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts b/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts deleted file mode 100644 index 1de5bed146e..00000000000 --- a/src/vscode-dts/vscode.proposed.envCollectionOptions.d.ts +++ /dev/null @@ -1,55 +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/179476 - - /** - * Options applied to the mutator. - */ - export interface EnvironmentVariableMutatorOptions { - /** - * Apply to the environment just before the process is created. - */ - applyAtProcessCreation?: boolean; - - /** - * Apply to the environment in the shell integration script. Note that this _will not_ apply - * the mutator if shell integration is disabled or not working for some reason. - */ - applyAtShellIntegration?: boolean; - } - - /** - * A type of mutation and its value to be applied to an environment variable. - */ - export interface EnvironmentVariableMutator { - /** - * Options applied to the mutator. - */ - readonly options: EnvironmentVariableMutatorOptions; - } - - export interface EnvironmentVariableCollection extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> { - /** - * @param options Options applied to the mutator, when not options are provided this will - * default to `{ applyAtProcessCreation: true }` - */ - replace(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; - - /** - * @param options Options applied to the mutator, when not options are provided this will - * default to `{ applyAtProcessCreation: true }` - */ - append(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; - - /** - * @param options Options applied to the mutator, when not options are provided this will - * default to `{ applyAtProcessCreation: true }` - */ - prepend(variable: string, value: string, options?: EnvironmentVariableMutatorOptions): void; - } -} From 2d3235f7474a807220f5a3c48219f92ab331c9e6 Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 23 Aug 2023 23:17:37 +0800 Subject: [PATCH 082/607] =?UTF-8?q?immediately=20search=20after=20enter=20?= =?UTF-8?q?pressed=20in=20files=20to=20include/exclude=20te=E2=80=A6=20(#1?= =?UTF-8?q?90473)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit immediately search after enter pressed in files to include/exclude text fields. --- src/vs/workbench/contrib/search/browser/searchView.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index cf7506c874c..6df2f295095 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1454,9 +1454,11 @@ export class SearchView extends ViewPane { if (options.triggeredOnType && !this.searchConfig.searchOnType) { return; } if (!this.pauseSearching) { + + const delay = options.triggeredOnType ? options.delay : 0; this.triggerQueryDelayer.trigger(() => { this._onQueryChanged(options.preserveFocus, options.triggeredOnType); - }, options.delay); + }, delay); } } From 2e95e033cf014820e60f382c759be2b25a231114 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 23 Aug 2023 08:20:33 -0700 Subject: [PATCH 083/607] support for fastAndSlow picks in quick search (#191002) --- .../browser/notebookSearchContributions.ts | 2 +- .../search/browser/notebookSearchService.ts | 83 +++++++----- .../quickTextSearch/textSearchQuickAccess.ts | 72 ++++++---- .../contrib/search/browser/replaceService.ts | 4 +- .../search/browser/search.contribution.ts | 4 +- .../contrib/search/browser/searchModel.ts | 114 +++++++++++----- .../contrib/search/browser/searchView.ts | 10 +- .../{browser => common}/notebookSearch.ts | 6 +- .../search/test/browser/searchModel.test.ts | 123 +++++++++++++----- .../searchEditor/browser/searchEditor.ts | 4 +- .../services/search/common/search.ts | 4 +- .../services/search/common/searchService.ts | 84 +++++++----- 12 files changed, 343 insertions(+), 167 deletions(-) rename src/vs/workbench/contrib/search/{browser => common}/notebookSearch.ts (75%) diff --git a/src/vs/workbench/contrib/search/browser/notebookSearchContributions.ts b/src/vs/workbench/contrib/search/browser/notebookSearchContributions.ts index 03b44bf0026..57d73a21c3f 100644 --- a/src/vs/workbench/contrib/search/browser/notebookSearchContributions.ts +++ b/src/vs/workbench/contrib/search/browser/notebookSearchContributions.ts @@ -7,7 +7,7 @@ import { ReplacePreviewContentProvider } from 'vs/workbench/contrib/search/brows import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { INotebookSearchService } from 'vs/workbench/contrib/search/browser/notebookSearch'; +import { INotebookSearchService } from 'vs/workbench/contrib/search/common/notebookSearch'; import { NotebookSearchService } from 'vs/workbench/contrib/search/browser/notebookSearchService'; export function registerContributions(): void { diff --git a/src/vs/workbench/contrib/search/browser/notebookSearchService.ts b/src/vs/workbench/contrib/search/browser/notebookSearchService.ts index adee7532d41..19b45f30903 100644 --- a/src/vs/workbench/contrib/search/browser/notebookSearchService.ts +++ b/src/vs/workbench/contrib/search/browser/notebookSearchService.ts @@ -18,10 +18,10 @@ import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/mode import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookExclusiveDocumentFilter, NotebookData } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { INotebookSearchService } from 'vs/workbench/contrib/search/browser/notebookSearch'; +import { INotebookSearchService } from 'vs/workbench/contrib/search/common/notebookSearch'; import { IFileMatchWithCells, ICellMatch, CellSearchModel, contentMatchesToTextSearchMatches, webviewMatchesToTextSearchMatches, genericCellMatchesToTextSearchMatches } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; import { IEditorResolverService, priorityToRank } from 'vs/workbench/services/editor/common/editorResolverService'; -import { ITextQuery, IFileQuery, QueryType, ISearchProgressItem, ISearchComplete, ISearchConfigurationProperties, ISearchService } from 'vs/workbench/services/search/common/search'; +import { ITextQuery, QueryType, ISearchProgressItem, ISearchComplete, ISearchConfigurationProperties, IFileQuery, ISearchService } from 'vs/workbench/services/search/common/search'; import * as arrays from 'vs/base/common/arrays'; import { isNumber } from 'vs/base/common/types'; @@ -123,48 +123,67 @@ export class NotebookSearchService implements INotebookSearchService { return Array.from(uris.keys()); } - async notebookSearch(query: ITextQuery, token: CancellationToken, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void): Promise<{ completeData: ISearchComplete; scannedFiles: ResourceSet }> { + notebookSearch(query: ITextQuery, token: CancellationToken | undefined, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void): { + openFilesToScan: ResourceSet; + completeData: Promise; + allScannedFiles: Promise; + } { if (query.type !== QueryType.Text) { return { - completeData: { + openFilesToScan: new ResourceSet(), + completeData: Promise.resolve({ messages: [], limitHit: false, results: [], - }, - scannedFiles: new ResourceSet() + }), + allScannedFiles: Promise.resolve(new ResourceSet()), }; } - const searchStart = Date.now(); const localNotebookWidgets = this.getLocalNotebookWidgets(); const localNotebookFiles = localNotebookWidgets.map(widget => widget.viewModel!.uri); - const localResultPromise = this.getLocalNotebookResults(query, token, localNotebookWidgets, searchInstanceID); - const searchLocalEnd = Date.now(); + const getAllResults = (): { completeData: Promise; allScannedFiles: Promise } => { + const searchStart = Date.now(); - const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental?.closedNotebookRichContentResults ?? false; + const localResultPromise = this.getLocalNotebookResults(query, token ?? CancellationToken.None, localNotebookWidgets, searchInstanceID); + const searchLocalEnd = Date.now(); - let closedResultsPromise: Promise = Promise.resolve(undefined); - if (experimentalNotebooksEnabled) { - closedResultsPromise = this.getClosedNotebookResults(query, new ResourceSet(localNotebookFiles, uri => this.uriIdentityService.extUri.getComparisonKey(uri)), token); - } + const experimentalNotebooksEnabled = this.configurationService.getValue('search').experimental?.closedNotebookRichContentResults ?? false; - const resolved = (await Promise.all([localResultPromise, closedResultsPromise])).filter((result): result is INotebookSearchMatchResults => !!result); - const resultArray = resolved.map(elem => elem.results); + let closedResultsPromise: Promise = Promise.resolve(undefined); + if (experimentalNotebooksEnabled) { + closedResultsPromise = this.getClosedNotebookResults(query, new ResourceSet(localNotebookFiles, uri => this.uriIdentityService.extUri.getComparisonKey(uri)), token ?? CancellationToken.None); + } - const results = arrays.coalesce(resultArray.flatMap(map => Array.from(map.values()))); - const scannedFiles = new ResourceSet(resultArray.flatMap(map => Array.from(map.keys())), uri => this.uriIdentityService.extUri.getComparisonKey(uri)); - if (onProgress) { - results.forEach(onProgress); - } - this.logService.trace(`local notebook search time | ${searchLocalEnd - searchStart}ms`); + const promise = Promise.all([localResultPromise, closedResultsPromise]); + return { + completeData: promise.then(resolvedPromise => { + const resolved = resolvedPromise.filter((e): e is INotebookSearchMatchResults => !!e); + const resultArray = resolved.map(elem => elem.results); + const results = arrays.coalesce(resultArray.flatMap(map => Array.from(map.values()))); + if (onProgress) { + results.forEach(onProgress); + } + this.logService.trace(`local notebook search time | ${searchLocalEnd - searchStart}ms`); + return { + messages: [], + limitHit: resolved.reduce((prev, cur) => prev || cur.limitHit, false), + results, + }; + }), + allScannedFiles: promise.then(resolvedPromise => { + const resolved = resolvedPromise.filter((e): e is INotebookSearchMatchResults => !!e); + const resultArray = resolved.map(elem => elem.results); + return new ResourceSet(resultArray.flatMap(map => Array.from(map.keys())), uri => this.uriIdentityService.extUri.getComparisonKey(uri)); + }) + }; + }; + const promiseResults = getAllResults(); return { - completeData: { - messages: [], - limitHit: resolved.reduce((prev, cur) => prev || cur.limitHit, false), - results, - }, - scannedFiles + openFilesToScan: new ResourceSet(localNotebookFiles), + completeData: promiseResults.completeData, + allScannedFiles: promiseResults.allScannedFiles }; } @@ -272,10 +291,10 @@ export class NotebookSearchService implements INotebookSearchService { regex: query.contentPattern.isRegExp, wholeWord: query.contentPattern.isWordMatch, caseSensitive: query.contentPattern.isCaseSensitive, - includeMarkupInput: query.contentPattern.notebookInfo?.isInNotebookMarkdownInput, - includeMarkupPreview: query.contentPattern.notebookInfo?.isInNotebookMarkdownPreview, - includeCodeInput: query.contentPattern.notebookInfo?.isInNotebookCellInput, - includeOutput: query.contentPattern.notebookInfo?.isInNotebookCellOutput, + includeMarkupInput: query.contentPattern.notebookInfo?.isInNotebookMarkdownInput ?? true, + includeMarkupPreview: query.contentPattern.notebookInfo?.isInNotebookMarkdownPreview ?? true, + includeCodeInput: query.contentPattern.notebookInfo?.isInNotebookCellInput ?? true, + includeOutput: query.contentPattern.notebookInfo?.isInNotebookCellOutput ?? true, }, token, false, true, searchID); diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index ad3d5da68ff..15a82a6cf6a 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -12,12 +12,12 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchCompressibleObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; -import { IPickerQuickAccessItem, PickerQuickAccessProvider } from 'vs/platform/quickinput/browser/pickerQuickAccess'; -import { IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { FastAndSlowPicks, IPickerQuickAccessItem, PickerQuickAccessProvider, Picks } from 'vs/platform/quickinput/browser/pickerQuickAccess'; +import { IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IViewsService } from 'vs/workbench/common/views'; import { searchDetailsIcon, searchOpenInFileIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; -import { Match, MatchInNotebook, RenderableMatch, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; +import { FileMatch, Match, MatchInNotebook, RenderableMatch, SearchModel, searchComparer } from 'vs/workbench/contrib/search/browser/searchModel'; import { SearchView, getEditorSelectionFromMatch } from 'vs/workbench/contrib/search/browser/searchView'; import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -75,8 +75,10 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider('search'); } - private async doSearch(contentPattern: string): Promise { - + private doSearch(contentPattern: string, token: CancellationToken): { + syncResults: FileMatch[]; + asyncResults: Promise; + } | undefined { if (contentPattern === '') { return undefined; } @@ -89,8 +91,16 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider folder.uri), this._getTextQueryBuilderOptions(charsPerLine)); - await this.searchModel.search(query, undefined); - return this.searchModel.searchResult; + const result = this.searchModel.search(query, undefined, token); + + const getAsyncResults = async () => { + await result.asyncResults; + return this.searchModel.searchResult.matches().filter(e => result.syncResults.indexOf(e) === -1); + }; + return { + syncResults: this.searchModel.searchResult.matches(), + asyncResults: getAsyncResults() + }; } private moveToSearchViewlet(model: SearchModel, currentElem: RenderableMatch) { @@ -107,31 +117,24 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - - const searchResult = await this.doSearch(contentPattern); - - if (!searchResult) { - return []; - } + private _getPicksFromMatches(matches: FileMatch[], limit: number): (IQuickPickSeparator | IPickerQuickAccessItem)[] { + matches = matches.sort(searchComparer); + const files = matches.length > limit ? matches.slice(0, limit) : matches; const picks: Array = []; - const matches = searchResult.matches(); - const files = matches.length > MAX_FILES_SHOWN ? matches.slice(0, MAX_FILES_SHOWN) : matches; - for (let fileIndex = 0; fileIndex < matches.length; fileIndex++) { - if (fileIndex === MAX_FILES_SHOWN) { + if (fileIndex === limit) { picks.push({ type: 'separator', }); picks.push({ - label: 'See More Files', + label: localize('QuickSearchSeeMoreFiles', "See More Files"), iconClass: ThemeIcon.asClassName(searchDetailsIcon), accept: async () => { - this.moveToSearchViewlet(this.searchModel, matches[MAX_FILES_SHOWN]); + this.moveToSearchViewlet(this.searchModel, matches[limit]); } }); break; @@ -159,7 +162,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { this.moveToSearchViewlet(this.searchModel, element); @@ -173,10 +176,10 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider | Promise | FastAndSlowPicks> | FastAndSlowPicks | null { + const allMatches = this.doSearch(contentPattern, token); + + if (!allMatches) { + return null; + } + const matches = allMatches.syncResults; + const syncResult = this._getPicksFromMatches(matches, MAX_FILES_SHOWN); + + if (matches.length >= MAX_FILES_SHOWN) { + return syncResult; } - return picks; + return { + picks: syncResult, + additionalPicks: allMatches.asyncResults.then((asyncResults) => { + return this._getPicksFromMatches(asyncResults, MAX_FILES_SHOWN - matches.length); + }) + }; } } diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index d45d1f289ed..8dd187bff0a 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -11,7 +11,7 @@ import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; -import { Match, FileMatch, FileMatchOrMatch, ISearchWorkbenchService, MatchInNotebook } from 'vs/workbench/contrib/search/browser/searchModel'; +import { Match, FileMatch, FileMatchOrMatch, ISearchViewModelWorkbenchService, MatchInNotebook } from 'vs/workbench/contrib/search/browser/searchModel'; import { IProgress, IProgressStep } from 'vs/platform/progress/common/progress'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -63,7 +63,7 @@ class ReplacePreviewModel extends Disposable { @ILanguageService private readonly languageService: ILanguageService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IReplaceService private readonly replaceService: IReplaceService, - @ISearchWorkbenchService private readonly searchWorkbenchService: ISearchWorkbenchService + @ISearchViewModelWorkbenchService private readonly searchWorkbenchService: ISearchViewModelWorkbenchService ) { super(); } diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 0aa08eff74d..a48332a48f2 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -27,7 +27,7 @@ import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; import { registerContributions as searchWidgetContributions } from 'vs/workbench/contrib/search/browser/searchWidget'; import { SymbolsQuickAccessProvider } from 'vs/workbench/contrib/search/browser/symbolsQuickAccess'; import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { ISearchWorkbenchService, SearchWorkbenchService } from 'vs/workbench/contrib/search/browser/searchModel'; +import { ISearchViewModelWorkbenchService, SearchViewModelWorkbenchService } from 'vs/workbench/contrib/search/browser/searchModel'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { SearchSortOrder, SEARCH_EXCLUDE_CONFIG, VIEWLET_ID, ViewMode, VIEW_ID } from 'vs/workbench/services/search/common/search'; import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; @@ -45,7 +45,7 @@ import 'vs/workbench/contrib/search/browser/searchActionsTopBar'; import 'vs/workbench/contrib/search/browser/searchActionsTextQuickAccess'; import { TEXT_SEARCH_QUICK_ACCESS_PREFIX, TextSearchQuickAccess } from 'vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess'; -registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, InstantiationType.Delayed); +registerSingleton(ISearchViewModelWorkbenchService, SearchViewModelWorkbenchService, InstantiationType.Delayed); registerSingleton(ISearchHistoryService, SearchHistoryService, InstantiationType.Delayed); replaceContributions(); diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 82bf57c7636..95a4644baf4 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -36,9 +36,9 @@ import { CellFindMatchWithIndex, CellWebviewFindMatch, ICellViewModel } from 'vs import { NotebookEditorWidget } from 'vs/workbench/contrib/notebook/browser/notebookEditorWidget'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { INotebookSearchService } from 'vs/workbench/contrib/search/browser/notebookSearch'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { CellSearchModel, ICellMatch, contentMatchesToTextSearchMatches, isIFileMatchWithCells, rawCellPrefix, webviewMatchesToTextSearchMatches } from 'vs/workbench/contrib/search/browser/searchNotebookHelpers'; +import { INotebookSearchService } from 'vs/workbench/contrib/search/common/notebookSearch'; import { ReplacePattern } from 'vs/workbench/services/search/common/replace'; import { IFileMatch, IPatternInfo, ISearchComplete, ISearchConfigurationProperties, ISearchProgressItem, ISearchRange, ISearchService, ITextQuery, ITextSearchContext, ITextSearchMatch, ITextSearchPreviewOptions, ITextSearchResult, ITextSearchStats, OneLineRange, resultIsMatch, SearchCompletionExitCode, SearchSortOrder } from 'vs/workbench/services/search/common/search'; import { addContextToEditorMatches, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; @@ -2002,34 +2002,63 @@ export class SearchModel extends Disposable { this._searchResultChangedListener = this._register(this._searchResult.onChange((e) => this._onSearchResultChanged.fire(e))); } - private async doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void): Promise { - const searchStart = Date.now(); - const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(); - const onProgressCall = (p: ISearchProgressItem) => { + + private doSearch(query: ITextQuery, progressEmitter: Emitter, searchQuery: ITextQuery, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void, callerToken?: CancellationToken): { + asyncResults: Promise; + syncResults: IFileMatch[]; + } { + const asyncGenerateOnProgress = async (p: ISearchProgressItem) => { progressEmitter.fire(); this.onSearchProgress(p, searchInstanceID); - onProgress?.(p); }; - const notebookResult = await this.notebookSearchService.notebookSearch(query, this.currentCancelTokenSource.token, searchInstanceID, onProgressCall); - const currentResult = await this.searchService.textSearch( + + const syncGenerateOnProgress = (p: ISearchProgressItem) => { + progressEmitter.fire(); + this.onSearchProgress(p, searchInstanceID, true); + onProgress?.(p); + }; + const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(callerToken); + + const notebookResult = this.notebookSearchService.notebookSearch(query, tokenSource.token, searchInstanceID, syncGenerateOnProgress); + const textResult = this.searchService.textSearchSplitSyncAsync( searchQuery, - this.currentCancelTokenSource.token, onProgressCall, - notebookResult?.scannedFiles + this.currentCancelTokenSource.token, asyncGenerateOnProgress, + notebookResult.openFilesToScan, + notebookResult.allScannedFiles, ); - tokenSource.dispose(); - const searchLength = Date.now() - searchStart; - this.logService.trace(`whole search time | ${searchLength}ms`); + + const syncResults = textResult.syncResults.results; + syncResults.forEach(p => { if (p) { syncGenerateOnProgress(p); } }); + + const getAsyncResults = async (): Promise => { + const searchStart = Date.now(); + + // resolve async parts of search + const allClosedEditorResults = await textResult.asyncResults; + const resolvedNotebookResults = await notebookResult.completeData; + tokenSource.dispose(); + const searchLength = Date.now() - searchStart; + const resolvedResult = { + results: [...allClosedEditorResults.results, ...resolvedNotebookResults.results], + messages: [...allClosedEditorResults.messages, ...resolvedNotebookResults.messages], + limitHit: allClosedEditorResults.limitHit || resolvedNotebookResults.limitHit, + exit: allClosedEditorResults.exit, + stats: allClosedEditorResults.stats, + }; + this.logService.trace(`whole search time | ${searchLength}ms`); + return resolvedResult; + }; return { - results: currentResult.results.concat(notebookResult.completeData.results), - messages: currentResult.messages.concat(notebookResult.completeData.messages), - limitHit: currentResult.limitHit || notebookResult.completeData.limitHit, - exit: currentResult.exit, - stats: currentResult.stats, + asyncResults: getAsyncResults(), + syncResults }; } - async search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { + search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void, callerToken?: CancellationToken): { + asyncResults: Promise; + syncResults: IFileMatch[]; + } { this.cancelSearch(true); this._searchQuery = query; @@ -2046,11 +2075,21 @@ export class SearchModel extends Disposable { // In search on type case, delay the streaming of results just a bit, so that we don't flash the only "local results" fast path this._startStreamDelay = new Promise(resolve => setTimeout(resolve, this.searchConfig.searchOnType ? 150 : 0)); - const currentRequest = this.doSearch(query, progressEmitter, this._searchQuery, searchInstanceID, onProgress); + const req = this.doSearch(query, progressEmitter, this._searchQuery, searchInstanceID, onProgress, callerToken); + const asyncResults = req.asyncResults; + const syncResults = req.syncResults; + + if (onProgress) { + syncResults.forEach(p => { + if (p) { + onProgress(p); + } + }); + } const start = Date.now(); - Promise.race([currentRequest, Event.toPromise(progressEmitter.event)]).finally(() => { + Promise.race([asyncResults, Event.toPromise(progressEmitter.event)]).finally(() => { /* __GDPR__ "searchResultsFirstRender" : { "owner": "roblourens", @@ -2060,12 +2099,14 @@ export class SearchModel extends Disposable { this.telemetryService.publicLog('searchResultsFirstRender', { duration: Date.now() - start }); }); - currentRequest.then( + asyncResults.then( value => this.onSearchCompleted(value, Date.now() - start, searchInstanceID), e => this.onSearchError(e, Date.now() - start)); - try { - return await currentRequest; + return { + asyncResults: asyncResults, + syncResults: syncResults + }; } finally { /* __GDPR__ "searchResultsFinished" : { @@ -2131,14 +2172,23 @@ export class SearchModel extends Disposable { } } - private async onSearchProgress(p: ISearchProgressItem, searchInstanceID: string) { + private onSearchProgress(p: ISearchProgressItem, searchInstanceID: string, sync = true) { if ((p).resource) { this._resultQueue.push(p); - await this._startStreamDelay; - if (this._resultQueue.length) { - this._searchResult.add(this._resultQueue, searchInstanceID, true); - this._resultQueue.length = 0; + if (sync) { + if (this._resultQueue.length) { + this._searchResult.add(this._resultQueue, searchInstanceID, true); + this._resultQueue.length = 0; + } + } else { + this._startStreamDelay.then(() => { + if (this._resultQueue.length) { + this._searchResult.add(this._resultQueue, searchInstanceID, true); + this._resultQueue.length = 0; + } + }); } + } } @@ -2171,7 +2221,7 @@ export type FileMatchOrMatch = FileMatch | Match; export type RenderableMatch = FolderMatch | FolderMatchWithResource | FileMatch | Match; -export class SearchWorkbenchService implements ISearchWorkbenchService { +export class SearchViewModelWorkbenchService implements ISearchViewModelWorkbenchService { declare readonly _serviceBrand: undefined; private _searchModel: SearchModel | null = null; @@ -2187,9 +2237,9 @@ export class SearchWorkbenchService implements ISearchWorkbenchService { } } -export const ISearchWorkbenchService = createDecorator('searchWorkbenchService'); +export const ISearchViewModelWorkbenchService = createDecorator('searchViewModelWorkbenchService'); -export interface ISearchWorkbenchService { +export interface ISearchViewModelWorkbenchService { readonly _serviceBrand: undefined; readonly searchModel: SearchModel; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 6df2f295095..d6be8a28998 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -70,7 +70,7 @@ import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { IReplaceService } from 'vs/workbench/contrib/search/browser/replace'; import { getOutOfWorkspaceEditorResources, SearchStateKey, SearchUIState } from 'vs/workbench/contrib/search/common/search'; import { ISearchHistoryService, ISearchHistoryValues, SearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; -import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchWorkbenchService, Match, MatchInNotebook, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; +import { FileMatch, FileMatchOrMatch, FolderMatch, FolderMatchWithResource, IChangeEvent, ISearchViewModelWorkbenchService, Match, MatchInNotebook, RenderableMatch, searchMatchComparer, SearchModel, SearchResult } from 'vs/workbench/contrib/search/browser/searchModel'; import { createEditorFromSearchResult } from 'vs/workbench/contrib/searchEditor/browser/searchEditorActions'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; @@ -174,7 +174,7 @@ export class SearchView extends ViewPane { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @ISearchWorkbenchService private readonly searchWorkbenchService: ISearchWorkbenchService, + @ISearchViewModelWorkbenchService private readonly searchViewModelWorkbenchService: ISearchViewModelWorkbenchService, @IContextKeyService contextKeyService: IContextKeyService, @IReplaceService private readonly replaceService: IReplaceService, @ITextFileService private readonly textFileService: ITextFileService, @@ -235,7 +235,7 @@ export class SearchView extends ViewPane { } }); - this.viewModel = this._register(this.searchWorkbenchService.searchModel); + this.viewModel = this._register(this.searchViewModelWorkbenchService.searchModel); this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); this.memento = new Memento(this.id, storageService); this.viewletState = this.memento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); @@ -1743,8 +1743,8 @@ export class SearchView extends ViewPane { this.tree.setSelection([]); this.tree.setFocus([]); - return this.viewModel.search(query) - .then(onComplete, onError); + const result = this.viewModel.search(query); + return result.asyncResults.then(onComplete, onError); } private onOpenSettings(e: dom.EventLike): void { diff --git a/src/vs/workbench/contrib/search/browser/notebookSearch.ts b/src/vs/workbench/contrib/search/common/notebookSearch.ts similarity index 75% rename from src/vs/workbench/contrib/search/browser/notebookSearch.ts rename to src/vs/workbench/contrib/search/common/notebookSearch.ts index 2fb52658219..5237c1bd03c 100644 --- a/src/vs/workbench/contrib/search/browser/notebookSearch.ts +++ b/src/vs/workbench/contrib/search/common/notebookSearch.ts @@ -14,5 +14,9 @@ export interface INotebookSearchService { readonly _serviceBrand: undefined; - notebookSearch(query: ITextQuery, token: CancellationToken, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void): Promise<{ completeData: ISearchComplete; scannedFiles: ResourceSet }>; + notebookSearch(query: ITextQuery, token: CancellationToken | undefined, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void): { + openFilesToScan: ResourceSet; + completeData: Promise; + allScannedFiles: Promise; + }; } diff --git a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index 0adcb1e47d4..ca0a09f0c31 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -14,7 +14,7 @@ import { ModelService } from 'vs/editor/common/services/modelService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextSearchMatch, OneLineRange, QueryType, TextSearchMatch } from 'vs/workbench/services/search/common/search'; +import { IFileMatch, IFileQuery, IFileSearchStats, IFolderQuery, ISearchComplete, ISearchProgressItem, ISearchQuery, ISearchService, ITextQuery, ITextSearchMatch, OneLineRange, QueryType, TextSearchMatch } from 'vs/workbench/services/search/common/search'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { CellMatch, MatchInNotebook, SearchModel } from 'vs/workbench/contrib/search/browser/searchModel'; @@ -36,7 +36,7 @@ import { ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBr import { FindMatch, IReadonlyTextBuffer } from 'vs/editor/common/model'; import { ResourceMap, ResourceSet } from 'vs/base/common/map'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; -import { INotebookSearchService } from 'vs/workbench/contrib/search/browser/notebookSearch'; +import { INotebookSearchService } from 'vs/workbench/contrib/search/common/notebookSearch'; const nullEvent = new class { id: number = -1; @@ -112,6 +112,20 @@ suite('SearchModel', () => { }); }); + }, + textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { + return { + syncResults: { + results: [], + messages: [] + }, + asyncResults: new Promise(resolve => { + queueMicrotask(() => { + results.forEach(onProgress!); + resolve(complete!); + }); + }) + }; } }; } @@ -129,6 +143,17 @@ suite('SearchModel', () => { reject(error); }); }); + }, + textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { + return { + syncResults: { + results: [], + messages: [] + }, + asyncResults: new Promise((resolve, reject) => { + reject(error); + }) + }; } }; } @@ -151,16 +176,47 @@ suite('SearchModel', () => { resolve({}); }); }); + }, + textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { + token?.onCancellationRequested(() => tokenSource.cancel()); + return { + syncResults: { + results: [], + messages: [] + }, + asyncResults: new Promise(resolve => { + queueMicrotask(() => { + resolve({}); + }); + }) + }; + } + }; + } + + function searchServiceWithDeferredPromise(p: Promise): ISearchService { + return { + textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined): { syncResults: ISearchComplete; asyncResults: Promise } { + return { + syncResults: { + results: [], + messages: [] + }, + asyncResults: p, + }; } }; } - function notebookSearchServiceWithInfo(results: IFileMatchWithCells[], tokenSource: CancellationTokenSource | undefined): INotebookSearchService { return { _serviceBrand: undefined, - notebookSearch(query: ISearchQuery, token: CancellationToken, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void, notebookURIs?: ResourceSet): Promise<{ completeData: ISearchComplete; scannedFiles: ResourceSet }> { + notebookSearch(query: ITextQuery, token: CancellationToken | undefined, searchInstanceID: string, onProgress?: (result: ISearchProgressItem) => void): { + openFilesToScan: ResourceSet; + completeData: Promise; + allScannedFiles: Promise; + } { token?.onCancellationRequested(() => tokenSource?.cancel()); const localResults = new ResourceMap(uri => uri.path); @@ -171,15 +227,15 @@ suite('SearchModel', () => { if (onProgress) { arrays.coalesce([...localResults.values()]).forEach(onProgress); } - return Promise.resolve( - { - completeData: { - messages: [], - results: arrays.coalesce([...localResults.values()]), - limitHit: false - }, - scannedFiles: new ResourceSet([...localResults.keys()]), - }); + return { + openFilesToScan: new ResourceSet([...localResults.keys()]), + completeData: Promise.resolve({ + messages: [], + results: arrays.coalesce([...localResults.values()]), + limitHit: false + }), + allScannedFiles: Promise.resolve(new ResourceSet()), + }; } }; } @@ -194,7 +250,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); - await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; const actual = testObject.searchResult.matches(); @@ -216,17 +272,14 @@ suite('SearchModel', () => { test('Search Model: Search can return notebook results', async () => { - const notebookUri = createFileUriFromPathFromRoot('/1'); - const results = [ aRawMatch('/2', new TextSearchMatch('test', new OneLineRange(1, 1, 5)), new TextSearchMatch('this is a test', new OneLineRange(1, 11, 15))), aRawMatch('/3', new TextSearchMatch('test', lineOneRange))]; - const searchService = instantiationService.stub(ISearchService, searchServiceWithResults(results, { limitHit: false, messages: [], results })); + instantiationService.stub(ISearchService, searchServiceWithResults(results, { limitHit: false, messages: [], results })); sinon.stub(CellMatch.prototype, 'addContext'); - const textSearch = sinon.spy(searchService, 'textSearch'); const mdInputCell = { cellKind: CellKind.Markup, textBuffer: { getLineContent(lineNumber: number): string { @@ -297,12 +350,10 @@ suite('SearchModel', () => { const notebookSearchService = instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([aRawMatchWithCells('/1', cellMatchMd, cellMatchCode)], undefined)); const notebookSearch = sinon.spy(notebookSearchService, "notebookSearch"); const model: SearchModel = instantiationService.createInstance(SearchModel); - await model.search({ contentPattern: { pattern: 'test' }, type: QueryType.Text, folderQueries }); + await model.search({ contentPattern: { pattern: 'test' }, type: QueryType.Text, folderQueries }).asyncResults; const actual = model.searchResult.matches(); assert(notebookSearch.calledOnce); - assert(textSearch.getCall(0).args[3]?.size === 1); - assert(textSearch.getCall(0).args[3]?.has(notebookUri)); // ensure that the textsearch knows not to re-source the notebooks assert.strictEqual(3, actual.length); assert.strictEqual(URI.file(`${getRootName()}/1`).toString(), actual[0].resource.toString()); @@ -351,7 +402,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); - await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; assert.ok(target.calledThrice); assert.ok(target.calledWith('searchResultsFirstRender')); @@ -368,7 +419,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); - const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); + const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; return result.then(() => { return timeout(1).then(() => { @@ -390,7 +441,7 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); - const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); + const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; return result.then(() => { return timeout(1).then(() => { @@ -410,15 +461,15 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, 'publicLog', target1); instantiationService.stub(ISearchService, searchServiceWithError(new Error('error'))); + instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); - const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); + const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; return result.then(() => { }, () => { return timeout(1).then(() => { assert.ok(target1.calledWith('searchResultsFirstRender')); assert.ok(target1.calledWith('searchResultsFinished')); - // assert.ok(target2.calledOnce); }); }); }); @@ -430,14 +481,16 @@ suite('SearchModel', () => { instantiationService.stub(ITelemetryService, 'publicLog', target1); const deferredPromise = new DeferredPromise(); - instantiationService.stub(ISearchService, 'textSearch', deferredPromise.p); + + instantiationService.stub(ISearchService, searchServiceWithDeferredPromise(deferredPromise.p)); + instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject = instantiationService.createInstance(SearchModel); - const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); + const result = testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; deferredPromise.cancel(); - return result.then(() => { }, () => { + return result.then(() => { }, async () => { return timeout(1).then(() => { assert.ok(target1.calledWith('searchResultsFirstRender')); assert.ok(target1.calledWith('searchResultsFinished')); @@ -456,7 +509,7 @@ suite('SearchModel', () => { instantiationService.stub(ISearchService, searchServiceWithResults(results, { limitHit: false, messages: [], results: [] })); instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); - await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 'somestring' }, type: QueryType.Text, folderQueries }).asyncResults; assert.ok(!testObject.searchResult.isEmpty()); instantiationService.stub(ISearchService, searchServiceWithResults([])); @@ -487,24 +540,24 @@ suite('SearchModel', () => { instantiationService.stub(INotebookSearchService, notebookSearchServiceWithInfo([], undefined)); const testObject: SearchModel = instantiationService.createInstance(SearchModel); - await testObject.search({ contentPattern: { pattern: 're' }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 're' }, type: QueryType.Text, folderQueries }).asyncResults; testObject.replaceString = 'hello'; let match = testObject.searchResult.matches()[0].matches()[0]; assert.strictEqual('hello', match.replaceString); - await testObject.search({ contentPattern: { pattern: 're', isRegExp: true }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 're', isRegExp: true }, type: QueryType.Text, folderQueries }).asyncResults; match = testObject.searchResult.matches()[0].matches()[0]; assert.strictEqual('hello', match.replaceString); - await testObject.search({ contentPattern: { pattern: 're(?:vi)', isRegExp: true }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 're(?:vi)', isRegExp: true }, type: QueryType.Text, folderQueries }).asyncResults; match = testObject.searchResult.matches()[0].matches()[0]; assert.strictEqual('hello', match.replaceString); - await testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: QueryType.Text, folderQueries }).asyncResults; match = testObject.searchResult.matches()[0].matches()[0]; assert.strictEqual('hello', match.replaceString); - await testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: QueryType.Text, folderQueries }); + await testObject.search({ contentPattern: { pattern: 'r(e)(?:vi)', isRegExp: true }, type: QueryType.Text, folderQueries }).asyncResults; testObject.replaceString = 'hello$1'; match = testObject.searchResult.matches()[0].matches()[0]; assert.strictEqual('helloe', match.replaceString); diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 783e8bea1dc..e9bc0f046cd 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -567,8 +567,8 @@ export class SearchEditor extends AbstractTextCodeEditor const { configurationModel } = await startInput.resolveModels(); configurationModel.updateConfig(config); - - startInput.ongoingSearchOperation = this.searchModel.search(query).finally(() => { + const result = this.searchModel.search(query); + startInput.ongoingSearchOperation = result.asyncResults.finally(() => { this.ongoingOperations--; if (this.ongoingOperations === 0) { this.searchOperation.stop(); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 1702df60753..caded264a85 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -19,6 +19,7 @@ import * as paths from 'vs/base/common/path'; import { isCancellationError } from 'vs/base/common/errors'; import { TextSearchCompleteMessageType } from 'vs/workbench/services/search/common/searchExtTypes'; import { isThenable } from 'vs/base/common/async'; +import { ResourceSet } from 'vs/base/common/map'; export { TextSearchCompleteMessageType }; @@ -41,7 +42,8 @@ export const ISearchService = createDecorator('searchService'); */ export interface ISearchService { readonly _serviceBrand: undefined; - textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void, notebookURIs?: Set): Promise; + textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise; + textSearchSplitSyncAsync(query: ITextQuery, token?: CancellationToken | undefined, onProgress?: ((result: ISearchProgressItem) => void) | undefined, notebookFilesToIgnore?: ResourceSet, asyncNotebookFilesToIgnore?: Promise): { syncResults: ISearchComplete; asyncResults: Promise }; fileSearch(query: IFileQuery, token?: CancellationToken): Promise; clearCache(cacheKey: string): Promise; registerSearchResultProvider(scheme: string, type: SearchProviderType, provider: ISearchResultProvider): IDisposable; diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 63b1c192a86..bbabce133c8 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -73,37 +73,63 @@ export class SearchService extends Disposable implements ISearchService { }); } - async textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void, notebookURIs?: ResourceSet): Promise { - // Get local results from dirty/untitled - const localResults = this.getLocalResults(query); + async textSearch(query: ITextQuery, token?: CancellationToken, onProgress?: (item: ISearchProgressItem) => void): Promise { + const results = this.textSearchSplitSyncAsync(query, token, onProgress); + const openEditorResults = results.syncResults; + const otherResults = await results.asyncResults; + return { + limitHit: otherResults.limitHit || openEditorResults.limitHit, + results: [...otherResults.results, ...openEditorResults.results], + messages: [...otherResults.messages, ...openEditorResults.messages] + }; + } + + textSearchSplitSyncAsync( + query: ITextQuery, + token?: CancellationToken | undefined, + onProgress?: ((result: ISearchProgressItem) => void) | undefined, + notebookFilesToIgnore?: ResourceSet, + asyncNotebookFilesToIgnore?: Promise + ): { + syncResults: ISearchComplete; + asyncResults: Promise; + } { + // Get open editor results from dirty/untitled + const openEditorResults = this.getOpenEditorResults(query); if (onProgress) { - arrays.coalesce([...localResults.results.values()]).forEach(onProgress); + arrays.coalesce([...openEditorResults.results.values()]).filter(e => !(notebookFilesToIgnore && notebookFilesToIgnore.has(e.resource))).forEach(onProgress); } - const onProviderProgress = (progress: ISearchProgressItem) => { - if (isFileMatch(progress)) { - // Match - if (!localResults.results.has(progress.resource) && !(notebookURIs && notebookURIs.has(progress.resource)) && onProgress) { // don't override local results - onProgress(progress); - } - } else if (onProgress) { - // Progress - onProgress(progress); - } - - if (isProgressMessage(progress)) { - this.logService.debug('SearchService#search', progress.message); - } + const syncResults: ISearchComplete = { + results: arrays.coalesce([...openEditorResults.results.values()]), + limitHit: openEditorResults.limitHit ?? false, + messages: [] + }; + + const getAsyncResults = async () => { + const resolvedAsyncNotebookFilesToIgnore = await asyncNotebookFilesToIgnore ?? new ResourceSet(); + const onProviderProgress = (progress: ISearchProgressItem) => { + if (isFileMatch(progress)) { + // Match + if (!openEditorResults.results.has(progress.resource) && !resolvedAsyncNotebookFilesToIgnore.has(progress.resource) && onProgress) { // don't override open editor results + onProgress(progress); + } + } else if (onProgress) { + // Progress + onProgress(progress); + } + + if (isProgressMessage(progress)) { + this.logService.debug('SearchService#search', progress.message); + } + }; + return await this.doSearch(query, token, onProviderProgress); }; - const otherResults = await this.doSearch(query, token, onProviderProgress); return { - ...otherResults, - ...{ - limitHit: otherResults.limitHit || localResults.limitHit - }, - results: [...otherResults.results, ...arrays.coalesce([...localResults.results.values()])] + syncResults, + asyncResults: getAsyncResults() }; } @@ -404,8 +430,8 @@ export class SearchService extends Disposable implements ISearchService { } } - private getLocalResults(query: ITextQuery): { results: ResourceMap; limitHit: boolean } { - const localResults = new ResourceMap(uri => this.uriIdentityService.extUri.getComparisonKey(uri)); + private getOpenEditorResults(query: ITextQuery): { results: ResourceMap; limitHit: boolean } { + const openEditorResults = new ResourceMap(uri => this.uriIdentityService.extUri.getComparisonKey(uri)); let limitHit = false; if (query.type === QueryType.Text) { @@ -465,18 +491,18 @@ export class SearchService extends Disposable implements ISearchService { } const fileMatch = new FileMatch(originalResource); - localResults.set(originalResource, fileMatch); + openEditorResults.set(originalResource, fileMatch); const textSearchResults = editorMatchesToTextSearchResults(matches, model, query.previewOptions); fileMatch.results = addContextToEditorMatches(textSearchResults, model, query); } else { - localResults.set(originalResource, null); + openEditorResults.set(originalResource, null); } }); } return { - results: localResults, + results: openEditorResults, limitHit }; } From ca2c1636f87ea4705f32345c2e348e815996e129 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 23 Aug 2023 17:09:57 +0200 Subject: [PATCH 084/607] Improves moved code detection feature --- .../diffEditorDecorations.ts | 15 +- .../diffEditorWidget2/diffEditorViewModel.ts | 28 ++- .../diffEditorWidget2.contribution.ts | 27 +++ .../diffEditorWidget2/diffEditorWidget2.ts | 25 ++- .../widget/diffEditorWidget2/lineAlignment.ts | 6 +- .../diffEditorWidget2/movedBlocksLines.ts | 164 ++++++++++++++++-- .../widget/diffEditorWidget2/style.css | 16 ++ .../browser/widget/diffEditorWidget2/utils.ts | 3 + src/vs/editor/common/editorContextKeys.ts | 1 + 9 files changed, 245 insertions(+), 40 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts index 194f37c9882..9a4b5bcf094 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts @@ -34,13 +34,13 @@ export class DiffEditorDecorations extends Disposable { return null; } - const currentMove = this._diffModel.read(reader)!.syncedMovedTexts.read(reader); + const movedTextToCompare = this._diffModel.read(reader)!.movedTextToCompare.read(reader); const renderIndicators = this._options.renderIndicators.read(reader); const showEmptyDecorations = this._options.showEmptyDecorations.read(reader); const originalDecorations: IModelDeltaDecoration[] = []; const modifiedDecorations: IModelDeltaDecoration[] = []; - if (!currentMove) { + if (!movedTextToCompare) { for (const m of diff.mappings) { if (!m.lineRangeMapping.originalRange.isEmpty) { originalDecorations.push({ range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); @@ -68,14 +68,14 @@ export class DiffEditorDecorations extends Disposable { } } - if (!m.lineRangeMapping.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !currentMove) { + if (!m.lineRangeMapping.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !movedTextToCompare) { modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modifiedRange.startLineNumber, 1)), options: arrowRevertChange }); } } } - if (currentMove) { - for (const m of currentMove.changes) { + if (movedTextToCompare) { + for (const m of movedTextToCompare.changes) { const fullRangeOriginal = m.originalRange.toInclusiveRange(); if (fullRangeOriginal) { originalDecorations.push({ range: fullRangeOriginal, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); @@ -91,12 +91,13 @@ export class DiffEditorDecorations extends Disposable { } } } + const activeMovedText = this._diffModel.read(reader)!.activeMovedText.read(reader); for (const m of diff.movedTexts) { originalDecorations.push({ range: m.lineRangeMapping.original.toInclusiveRange()!, options: { description: 'moved', - blockClassName: 'movedOriginal' + (m === currentMove ? ' currentMove' : ''), + blockClassName: 'movedOriginal' + (m === activeMovedText ? ' currentMove' : ''), blockPadding: [MovedBlocksLinesPart.movedCodeBlockPadding, 0, MovedBlocksLinesPart.movedCodeBlockPadding, MovedBlocksLinesPart.movedCodeBlockPadding], } }); @@ -104,7 +105,7 @@ export class DiffEditorDecorations extends Disposable { modifiedDecorations.push({ range: m.lineRangeMapping.modified.toInclusiveRange()!, options: { description: 'moved', - blockClassName: 'movedModified' + (m === currentMove ? ' currentMove' : ''), + blockClassName: 'movedModified' + (m === activeMovedText ? ' currentMove' : ''), blockPadding: [4, 0, 4, 4], } }); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 4927ebeb9c1..8dea4d6cc56 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -48,7 +48,21 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo } ); - public readonly syncedMovedTexts = observableValue('syncedMovedText', undefined); + public readonly movedTextToCompare = observableValue('movedTextToCompare', undefined); + + private readonly _activeMovedText = observableValue('activeMovedText', undefined); + private readonly _hoveredMovedText = observableValue('hoveredMovedText', undefined); + + + public readonly activeMovedText = derived(r => this.movedTextToCompare.read(r) ?? this._hoveredMovedText.read(r) ?? this._activeMovedText.read(r)); + + public setActiveMovedText(movedText: MovedText | undefined): void { + this._activeMovedText.set(movedText, undefined); + } + + public setHoveredMovedText(movedText: MovedText | undefined): void { + this._hoveredMovedText.set(movedText, undefined); + } constructor( public readonly model: IDiffEditorModel, @@ -114,8 +128,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo transaction(tx => { this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); updateUnchangedRegions(result, tx); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); + const currentSyncedMovedText = this.movedTextToCompare.get(); + this.movedTextToCompare.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); }); } } @@ -132,8 +146,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo transaction(tx => { this._diff.set(DiffState.fromDiffResult(this._lastDiff!), tx); updateUnchangedRegions(result, tx); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); + const currentSyncedMovedText = this.movedTextToCompare.get(); + this.movedTextToCompare.set(currentSyncedMovedText ? this._lastDiff!.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); }); } } @@ -180,8 +194,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const state = DiffState.fromDiffResult(result); this._diff.set(state, tx); this._isDiffUpToDate.set(true, tx); - const currentSyncedMovedText = this.syncedMovedTexts.get(); - this.syncedMovedTexts.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); + const currentSyncedMovedText = this.movedTextToCompare.get(); + this.movedTextToCompare.set(currentSyncedMovedText ? this._lastDiff.moves.find(m => m.lineRangeMapping.modified.intersect(currentSyncedMovedText.lineRangeMapping.modified)) : undefined, tx); }); })); } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts index 13c654d867f..7421abcd386 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from 'vs/base/common/codicons'; +import { KeyCode } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { findFocusedDiffEditor } from 'vs/editor/browser/widget/diffEditor.contribution'; @@ -128,3 +129,29 @@ export class SwitchSide extends EditorAction2 { } registerAction2(SwitchSide); + +export class ExitCompareMove extends EditorAction2 { + constructor() { + super({ + id: 'diffEditor.exitCompareMove', + title: { value: localize('exitCompareMove', "Exit Compare Move"), original: 'Exit Compare Move' }, + icon: Codicon.close, + precondition: EditorContextKeys.comparingMovedCode, + f1: false, + category: diffEditorCategory, + keybinding: { + weight: 10000, + primary: KeyCode.Escape, + } + }); + } + + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + const diffEditor = findFocusedDiffEditor(accessor); + if (diffEditor instanceof DiffEditorWidget2) { + diffEditor.exitCompareMove(); + } + } +} + +registerAction2(ExitCompareMove); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 63973ae2e03..5bc2b098e2b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -111,6 +111,12 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { isEmbeddedDiffEditorKey.set(this._options.isInEmbeddedEditor.read(reader)); })); + const comparingMovedCodeKey = EditorContextKeys.comparingMovedCode.bindTo(this._contextKeyService); + this._register(autorun(reader => { + /** @description update comparingMovedCodeKey */ + comparingMovedCodeKey.set(!!this._diffModel.read(reader)?.movedTextToCompare.read(reader)); + })); + const diffEditorRenderSideBySideInlineBreakpointReachedContextKeyValue = EditorContextKeys.diffEditorRenderSideBySideInlineBreakpointReached.bindTo(this._contextKeyService); this._register(autorun(reader => { /** @description update accessibleDiffViewerVisible context key */ @@ -223,19 +229,6 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { ), })); - this._register(this._editors.original.onDidChangeCursorPosition(e => { - const m = this._diffModel.get(); - if (!m) { return; } - const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.original.contains(e.position.lineNumber)); - m.syncedMovedTexts.set(movedText, undefined); - })); - this._register(this._editors.modified.onDidChangeCursorPosition(e => { - const m = this._diffModel.get(); - if (!m) { return; } - const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modified.contains(e.position.lineNumber)); - m.syncedMovedTexts.set(movedText, undefined); - })); - // Revert change when an arrow is clicked. this._register(this._editors.modified.onMouseDown(event => { if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('arrow-revert-change')) { @@ -518,6 +511,12 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { } destination.focus(); } + + exitCompareMove(): void { + const model = this._diffModel.get(); + if (!model) { return; } + model.movedTextToCompare.set(undefined, undefined); + } } function translatePosition(posInOriginal: Position, mappings: LineRangeMapping[]): Range { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts index 3a95ce28942..56357753f62 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts @@ -103,7 +103,7 @@ export class ViewZoneManager extends Disposable { const alignmentsSyncedMovedText = derived((reader) => { /** @description alignments */ - const syncedMovedText = this._diffModel.read(reader)?.syncedMovedTexts.read(reader); + const syncedMovedText = this._diffModel.read(reader)?.movedTextToCompare.read(reader); if (!syncedMovedText) { return null; } state.read(reader); const mappings = syncedMovedText.changes.map(c => new DiffMapping(c)); @@ -171,7 +171,7 @@ export class ViewZoneManager extends Disposable { const modLineHeight = this._editors.modified.getOption(EditorOption.lineHeight); - const syncedMovedText = this._diffModel.read(reader)?.syncedMovedTexts.read(reader); + const syncedMovedText = this._diffModel.read(reader)?.movedTextToCompare.read(reader); const mightContainNonBasicASCII = this._editors.original.getModel()?.mightContainNonBasicASCII() ?? false; const mightContainRTL = this._editors.original.getModel()?.mightContainRTL() ?? false; @@ -424,7 +424,7 @@ export class ViewZoneManager extends Disposable { this._register(autorun(reader => { /** @description update editor top offsets */ - const m = this._diffModel.read(reader)?.syncedMovedTexts.read(reader); + const m = this._diffModel.read(reader)?.movedTextToCompare.read(reader); let deltaOrigToMod = 0; if (m) { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 4453cb7ce2d..6a5316decba 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -3,15 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { h } from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Action } from 'vs/base/common/actions'; import { booleanComparator, compareBy, findMaxIdxBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; +import { Codicon } from 'vs/base/common/codicons'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, derived, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable'; +import { IObservable, autorun, autorunWithStore, constObservable, derived, derivedWithStore, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable'; +import { ThemeIcon } from 'vs/base/common/themables'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; +import { PlaceholderViewZone, ViewZoneOverlayWidget, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange, OffsetRangeSet } from 'vs/editor/common/core/offsetRange'; +import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { localize } from 'vs/nls'; export class MovedBlocksLinesPart extends Disposable { public static readonly movedCodeBlockPadding = 4; @@ -51,9 +59,50 @@ export class MovedBlocksLinesPart extends Disposable { })); this._register(keepAlive(this._state, true)); + + const movedBlockViewZones = derived(reader => { + const model = this._diffModel.read(reader); + const d = model?.diff.read(reader); + if (!d) { return []; } + return d.movedTexts.map(move => ({ + move, + original: new PlaceholderViewZone(constObservable(move.lineRangeMapping.original.startLineNumber - 1), 18), + modified: new PlaceholderViewZone(constObservable(move.lineRangeMapping.modified.startLineNumber - 1), 18), + })); + }); + + this._register(applyViewZones(this._editors.original, movedBlockViewZones.map(zones => zones.map(z => z.original)))); + this._register(applyViewZones(this._editors.modified, movedBlockViewZones.map(zones => zones.map(z => z.modified)))); + + this._register(autorunWithStore((reader, store) => { + const blocks = movedBlockViewZones.read(reader); + for (const b of blocks) { + store.add(new MovedBlockOverlayWidget(this._editors.original, b.original, b.move, 'original', this._diffModel.get()!)); + store.add(new MovedBlockOverlayWidget(this._editors.modified, b.modified, b.move, 'modified', this._diffModel.get()!)); + } + })); + + this._register(this._editors.original.onDidChangeCursorPosition(e => { + const m = this._diffModel.get(); + if (!m) { return; } + const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.original.contains(e.position.lineNumber)); + if (movedText !== m.movedTextToCompare.get()) { + m.movedTextToCompare.set(undefined, undefined); + } + m.setActiveMovedText(movedText); + })); + this._register(this._editors.modified.onDidChangeCursorPosition(e => { + const m = this._diffModel.get(); + if (!m) { return; } + const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modified.contains(e.position.lineNumber)); + if (movedText !== m.movedTextToCompare.get()) { + m.movedTextToCompare.set(undefined, undefined); + } + m.setActiveMovedText(movedText); + })); } - private readonly _state = derived(reader => { + private readonly _state = derivedWithStore('state', (reader, store) => { /** @description update moved blocks lines */ this._element.replaceChildren(); @@ -125,21 +174,35 @@ export class MovedBlocksLinesPart extends Disposable { rect.setAttribute('height', `${rectHeight}`); this._element.appendChild(rect); + const g = document.createElementNS('http://www.w3.org/2000/svg', 'g'); + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - if (line.move === model.syncedMovedTexts.read(reader)) { - path.classList.add('currentMove'); - } + path.setAttribute('d', `M ${0} ${line.from} L ${verticalY} ${line.from} L ${verticalY} ${line.to} L ${right - arrowWidth} ${line.to}`); path.setAttribute('fill', 'none'); - this._element.appendChild(path); + g.appendChild(path); const arrowRight = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); arrowRight.classList.add('arrow'); - if (line.move === model.syncedMovedTexts.read(reader)) { - arrowRight.classList.add('currentMove'); - } + + store.add(autorun(reader => { + path.classList.toggle('currentMove', line.move === model.activeMovedText.read(reader)); + arrowRight.classList.toggle('currentMove', line.move === model.activeMovedText.read(reader)); + })); + arrowRight.setAttribute('points', `${right - arrowWidth},${line.to - arrowHeight / 2} ${right},${line.to} ${right - arrowWidth},${line.to + arrowHeight / 2}`); - this._element.appendChild(arrowRight); + g.appendChild(arrowRight); + + this._element.appendChild(g); + + /* + TODO@hediet + path.addEventListener('mouseenter', () => { + model.setHoveredMovedText(line.move); + }); + path.addEventListener('mouseleave', () => { + model.setHoveredMovedText(undefined); + });*/ idx++; } @@ -184,3 +247,84 @@ class LinesLayout { return this._trackCount; } } + +class MovedBlockOverlayWidget extends ViewZoneOverlayWidget { + private readonly _nodes = h('div.diff-moved-code-block', { style: { marginRight: '4px' } }, [ + h('div.text-content@textContent'), + h('div.action-bar@actionBar'), + ]); + + constructor( + private readonly _editor: ICodeEditor, + _viewZone: PlaceholderViewZone, + private readonly _move: MovedText, + private readonly _kind: 'original' | 'modified', + private readonly _diffModel: DiffEditorViewModel, + ) { + const root = h('div.diff-hidden-lines-widget'); + super(_editor, _viewZone, root.root); + root.root.appendChild(this._nodes.root); + + const editorLayout = observableFromEvent(this._editor.onDidLayoutChange, () => this._editor.getLayoutInfo()); + + this._register(applyStyle(this._nodes.root, { + paddingRight: editorLayout.map(l => l.verticalScrollbarWidth) + })); + + let text: string; + + if (_move.changes.length > 0) { + text = this._kind === 'original' ? localize( + 'codeMovedToWithChanges', + 'Code moved with changes to line {0}-{1}', + this._move.lineRangeMapping.modified.startLineNumber, + this._move.lineRangeMapping.modified.endLineNumberExclusive + ) : localize( + 'codeMovedFromWithChanges', + 'Code moved with changes from line {0}-{1}', + this._move.lineRangeMapping.original.startLineNumber, + this._move.lineRangeMapping.original.endLineNumberExclusive + ); + } else { + text = this._kind === 'original' ? localize( + 'codeMovedTo', + 'Code moved to line {0}-{1}', + this._move.lineRangeMapping.modified.startLineNumber, + this._move.lineRangeMapping.modified.endLineNumberExclusive + ) : localize( + 'codeMovedFrom', + 'Code moved from line {0}-{1}', + this._move.lineRangeMapping.original.startLineNumber, + this._move.lineRangeMapping.original.endLineNumberExclusive + ); + } + + const actionBar = this._register(new ActionBar(this._nodes.actionBar, { + highlightToggledItems: true, + })); + + const caption = new Action( + '', + text, + '', + false, + ); + actionBar.push(caption, { icon: false, label: true }); + + const actionCompare = new Action( + '', + 'Compare', + ThemeIcon.asClassName(Codicon.compareChanges), + true, + () => { + this._editor.focus(); + this._diffModel.movedTextToCompare.set(this._diffModel.movedTextToCompare.get() ? undefined : this._move, undefined); + }, + ); + this._register(autorun(reader => { + const isActive = this._diffModel.movedTextToCompare.read(reader) === _move; + actionCompare.checked = isActive; + })); + actionBar.push(actionCompare, { icon: true, label: false }); + } +} diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/style.css b/src/vs/editor/browser/widget/diffEditorWidget2/style.css index 8fd6377362f..aa681f8f617 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/style.css +++ b/src/vs/editor/browser/widget/diffEditorWidget2/style.css @@ -86,6 +86,10 @@ stroke: var(--vscode-diffEditor-moveActive-border); } +.monaco-diff-editor .moved-blocks-lines path { + pointer-events: visiblestroke; +} + .monaco-diff-editor .moved-blocks-lines .arrow { fill: var(--vscode-diffEditor-move-border); } @@ -121,3 +125,15 @@ .monaco-editor .fold-unchanged { cursor: pointer; } + +.monaco-diff-editor .diff-moved-code-block { + display: flex; + justify-content: flex-end; + margin-top: -4px; +} + +.monaco-diff-editor .diff-moved-code-block .action-bar .action-label.codicon { + width: 12px; + height: 12px; + font-size: 12px; +} diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts index 9a86bfd448b..8460b24326e 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts @@ -268,6 +268,8 @@ export interface CSSStyle { top: number | string; visibility: 'visible' | 'hidden' | 'collapse'; display: 'block' | 'inline' | 'inline-block' | 'flex' | 'none'; + paddingLeft: number | string; + paddingRight: number | string; } export function applyStyle(domNode: HTMLElement, style: Partial<{ [TKey in keyof CSSStyle]: CSSStyle[TKey] | IObservable | undefined }>) { @@ -280,6 +282,7 @@ export function applyStyle(domNode: HTMLElement, style: Partial<{ [TKey in keyof if (typeof val === 'number') { val = `${val}px`; } + key = key.replace(/[A-Z]/g, m => '-' + m.toLowerCase()); domNode.style[key as any] = val as any; } }); diff --git a/src/vs/editor/common/editorContextKeys.ts b/src/vs/editor/common/editorContextKeys.ts index c831d05fe21..a2b1a88cc44 100644 --- a/src/vs/editor/common/editorContextKeys.ts +++ b/src/vs/editor/common/editorContextKeys.ts @@ -27,6 +27,7 @@ export namespace EditorContextKeys { export const readOnly = new RawContextKey('editorReadonly', false, nls.localize('editorReadonly', "Whether the editor is read-only")); export const inDiffEditor = new RawContextKey('inDiffEditor', false, nls.localize('inDiffEditor', "Whether the context is a diff editor")); export const isEmbeddedDiffEditor = new RawContextKey('isEmbeddedDiffEditor', false, nls.localize('isEmbeddedDiffEditor', "Whether the context is an embedded diff editor")); + export const comparingMovedCode = new RawContextKey('comparingMovedCode', false, nls.localize('comparingMovedCode', "Whether a moved code block is selected for comparison")); export const accessibleDiffViewerVisible = new RawContextKey('accessibleDiffViewerVisible', false, nls.localize('accessibleDiffViewerVisible', "Whether the accessible diff viewer is visible")); export const diffEditorRenderSideBySideInlineBreakpointReached = new RawContextKey('diffEditorRenderSideBySideInlineBreakpointReached', false, nls.localize('diffEditorRenderSideBySideInlineBreakpointReached', "Whether the diff editor render side by side inline breakpoint is reached")); export const columnSelection = new RawContextKey('editorColumnSelection', false, nls.localize('editorColumnSelection', "Whether `editor.columnSelection` is enabled")); From 6225119b0ca825fd668780367894d9f6c6d8d9a6 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 23 Aug 2023 09:13:21 -0700 Subject: [PATCH 085/607] Show progress in chat placeholder content (#190782) * Show progress in chat placeholder content * Make property names consistent * Rename more properties * Address PR comments * Dispose of markdown render result * Look up `isPlaceholder` on `currentResponseData` --- .../contrib/chat/browser/chatListRenderer.ts | 28 ++++++++++++++++--- .../contrib/chat/browser/media/chat.css | 15 ++++++++++ .../contrib/chat/common/chatModel.ts | 21 ++++++++------ 3 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 52f971ad1b9..dad24e99b60 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -72,6 +72,7 @@ import { createFileIconThemableTreeContainerScope } from 'vs/workbench/contrib/f import { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { distinct } from 'vs/base/common/arrays'; +import { IPlaceholderMarkdownString } from 'vs/workbench/contrib/chat/common/chatModel'; const $ = dom.$; @@ -414,7 +415,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer content.dispose() }; + } + private renderMarkdown(markdown: IMarkdownString, element: ChatTreeItem, disposables: DisposableStore, templateData: IChatListItemTemplate, fillInIncompleteTokens = false): IMarkdownRenderResult { const disposablesList: IDisposable[] = []; let codeBlockIndex = 0; @@ -622,7 +638,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer): IWordCountResult | undefined { const rate = this.getProgressiveRenderRate(element); const numWordsToRender = renderData.lastRenderTime === 0 ? 1 : @@ -1189,3 +1205,7 @@ class ChatListTreeDataSource implements IAsyncDataSource; updateContent(responsePart: string | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void; asString(): string; @@ -88,8 +88,11 @@ export class ChatRequestModel implements IChatRequestModel { } } +export interface IPlaceholderMarkdownString extends IMarkdownString { + isPlaceholder: boolean; +} -type ResponsePart = { string: IMarkdownString; resolving?: boolean } | { treeData: IChatResponseProgressFileTreeData; resolving?: boolean }; +type ResponsePart = { string: IMarkdownString; isPlaceholder?: boolean } | { treeData: IChatResponseProgressFileTreeData; isPlaceholder?: undefined }; export class Response implements IResponse { private _onDidChangeValue = new Emitter(); public get onDidChangeValue() { @@ -99,11 +102,11 @@ export class Response implements IResponse { // responseParts internally tracks all the response parts, including strings which are currently resolving, so that they can be updated when they do resolve private _responseParts: ResponsePart[]; // responseData externally presents the response parts with consolidated contiguous strings (including strings which were previously resolving) - private _responseData: (IMarkdownString | IChatResponseProgressFileTreeData)[]; + private _responseData: (IMarkdownString | IPlaceholderMarkdownString | IChatResponseProgressFileTreeData)[]; // responseRepr externally presents the response parts with consolidated contiguous strings (excluding tree data) private _responseRepr: string; - get value(): (IMarkdownString | IChatResponseProgressFileTreeData)[] { + get value(): (IMarkdownString | IPlaceholderMarkdownString | IChatResponseProgressFileTreeData)[] { return this._responseData; } @@ -127,7 +130,7 @@ export class Response implements IResponse { const responsePartLength = this._responseParts.length - 1; const lastResponsePart = this._responseParts[responsePartLength]; - if (lastResponsePart.resolving === true || isCompleteInteractiveProgressTreeData(lastResponsePart)) { + if (lastResponsePart.isPlaceholder === true || isCompleteInteractiveProgressTreeData(lastResponsePart)) { // The last part is resolving or a tree data item, start a new part this._responseParts.push({ string: new MarkdownString(responsePart) }); } else { @@ -138,16 +141,16 @@ export class Response implements IResponse { this._updateRepr(quiet); } else if ('placeholder' in responsePart) { // Add a new resolving part - const responsePosition = this._responseParts.push({ string: new MarkdownString(responsePart.placeholder), resolving: true }) - 1; + const responsePosition = this._responseParts.push({ string: new MarkdownString(responsePart.placeholder), isPlaceholder: true }) - 1; this._updateRepr(quiet); responsePart.resolvedContent?.then((content) => { // Replace the resolving part's content with the resolved response if (typeof content === 'string') { - this._responseParts[responsePosition] = { string: new MarkdownString(content), resolving: true }; + this._responseParts[responsePosition] = { string: new MarkdownString(content), isPlaceholder: true }; this._updateRepr(quiet); } else if (content.treeData) { - this._responseParts[responsePosition] = { treeData: content.treeData, resolving: true }; + this._responseParts[responsePosition] = { treeData: content.treeData }; this._updateRepr(quiet); } }); @@ -160,6 +163,8 @@ export class Response implements IResponse { this._responseData = this._responseParts.map(part => { if (isCompleteInteractiveProgressTreeData(part)) { return part.treeData; + } else if (part.isPlaceholder) { + return { ...part.string, isPlaceholder: true }; } return part.string; }); From 702a76bf5da92be1c91954c41b850f9c2f15d5a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20Ate=C5=9F=20Uzun?= Date: Wed, 23 Aug 2023 19:16:16 +0300 Subject: [PATCH 086/607] fix: localize string typo (#191046) * fix: localize string typo * fix: suffix typo --- .../contrib/extensions/browser/extensionsViews.ts | 2 +- .../contrib/files/browser/views/explorerViewer.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index f8d7c21cea7..fc8215dff5b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -1431,7 +1431,7 @@ export function getAriaLabelForExtension(extension: IExtension | null): string { if (!extension) { return ''; } - const publisher = extension.publisherDomain?.verified ? localize('extension.arialabel.verifiedPublihser', "Verified Publisher {0}", extension.publisherDisplayName) : localize('extension.arialabel.publihser', "Publisher {0}", extension.publisherDisplayName); + const publisher = extension.publisherDomain?.verified ? localize('extension.arialabel.verifiedPublisher', "Verified Publisher {0}", extension.publisherDisplayName) : localize('extension.arialabel.publisher', "Publisher {0}", extension.publisherDisplayName); const deprecated = extension?.deprecationInfo ? localize('extension.arialabel.deprecated', "Deprecated") : ''; const rating = extension?.rating ? localize('extension.arialabel.rating', "Rated {0} out of 5 stars by {1} users", extension.rating.toFixed(2), extension.ratingCount) : ''; return `${extension.displayName}, ${deprecated ? `${deprecated}, ` : ''}${extension.version}, ${publisher}, ${extension.description} ${rating ? `, ${rating}` : ''}`; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 6560e68e9a5..a25c052188a 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -1391,11 +1391,11 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const resourceEdit = new ResourceFileEdit(resource, newResource, { copy: true, overwrite: allowOverwrite }); resourceFileEdits.push(resourceEdit); } - const labelSufix = getFileOrFolderLabelSufix(sources); + const labelSuffix = getFileOrFolderLabelSuffix(sources); await this.explorerService.applyBulkEdit(resourceFileEdits, { confirmBeforeUndo: explorerConfig.confirmUndo === UndoConfirmLevel.Default || explorerConfig.confirmUndo === UndoConfirmLevel.Verbose, - undoLabel: localize('copy', "Copy {0}", labelSufix), - progressLabel: localize('copying', "Copying {0}", labelSufix), + undoLabel: localize('copy', "Copy {0}", labelSuffix), + progressLabel: localize('copying', "Copying {0}", labelSuffix), }); const editors = resourceFileEdits.filter(edit => { @@ -1410,11 +1410,11 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Do not allow moving readonly items const resourceFileEdits = sources.filter(source => !source.isReadonly).map(source => new ResourceFileEdit(source.resource, joinPath(target.resource, source.name))); - const labelSufix = getFileOrFolderLabelSufix(sources); + const labelSuffix = getFileOrFolderLabelSuffix(sources); const options = { confirmBeforeUndo: this.configurationService.getValue().explorer.confirmUndo === UndoConfirmLevel.Verbose, - undoLabel: localize('move', "Move {0}", labelSufix), - progressLabel: localize('moving', "Moving {0}", labelSufix) + undoLabel: localize('move', "Move {0}", labelSuffix), + progressLabel: localize('moving', "Moving {0}", labelSuffix) }; try { @@ -1518,7 +1518,7 @@ export class ExplorerCompressionDelegate implements ITreeCompressionDelegate Date: Wed, 23 Aug 2023 10:18:57 -0700 Subject: [PATCH 087/607] =?UTF-8?q?Broken=20`search.action.replace`=20defa?= =?UTF-8?q?ult=20mac=20shortcut=20(`=E2=87=A7`+`=E2=8C=98`+`1`)=20(#191102?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #189866 --- src/vs/workbench/contrib/search/browser/searchView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index d6be8a28998..07576765667 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -903,8 +903,8 @@ export class SearchView extends ViewPane { } let editable = false; - if (focus instanceof MatchInNotebook) { - editable = !focus.isWebviewMatch(); + if (focus instanceof Match) { + editable = (focus instanceof MatchInNotebook) ? !focus.isWebviewMatch() : true; } else if (focus instanceof FileMatch) { editable = !focus.hasOnlyReadOnlyMatches(); } else if (focus instanceof FolderMatch) { From bd67b50dfdf98e56bd79790ab7e2bc5a292ccdb4 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 23 Aug 2023 19:28:19 +0200 Subject: [PATCH 088/607] Remove tree item checkbox proposal from tests (#191084) Fixes #191081 --- extensions/vscode-api-tests/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 9d50e393547..a28d4f806aa 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -45,8 +45,7 @@ "textSearchProvider", "timeline", "tokenInformation", - "treeItemCheckbox", - "treeViewActiveItem", + "treeViewActiveItem", "treeViewReveal", "workspaceTrust", "telemetry", From 01857efa503aeed4efeada51aded87d742399dee Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 10:29:59 -0700 Subject: [PATCH 089/607] allow different keybindings --- .../contrib/terminal/common/terminal.ts | 1 + .../terminal/common/terminalContextKey.ts | 4 +++ .../terminal/common/terminalStrings.ts | 4 +++ .../terminal.accessibility.contribution.ts | 32 +++++++++++++++++-- .../browser/terminalAccessibleBuffer.ts | 2 +- .../browser/terminalAccessibleWidget.ts | 19 +++++++++-- 6 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 77e37c5b574..6402859667a 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -495,6 +495,7 @@ export const enum TerminalCommandId { HideSuggestWidget = 'workbench.action.terminal.hideSuggestWidget', FocusHover = 'workbench.action.terminal.focusHover', ShowEnvironmentContributions = 'workbench.action.terminal.showEnvironmentContributions', + FocusAndHideAccessibleBuffer = 'workbench.action.terminal.focusAndHideAccessibleBuffer', // Developer commands diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 1d480dbd649..ce4f7a4168c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -16,6 +16,7 @@ export const enum TerminalContextKeyStrings { Focus = 'terminalFocus', FocusInAny = 'terminalFocusInAny', AccessibleBufferFocus = 'terminalAccessibleBufferFocus', + AccessibleBufferOnLastLine = 'terminalAccessibleBufferOnLastLine', EditorFocus = 'terminalEditorFocus', TabsFocus = 'terminalTabsFocus', WebExtensionContributedProfile = 'terminalWebExtensionContributedProfile', @@ -51,6 +52,9 @@ export namespace TerminalContextKeys { /** Whether the accessible buffer is focused. */ export const accessibleBufferFocus = new RawContextKey(TerminalContextKeyStrings.AccessibleBufferFocus, false, localize('terminalAccessibleBufferFocusContextKey', "Whether the terminal accessible buffer is focused.")); + /** Whether the accessible buffer focus is on the last line. */ + export const accessibleBufferOnLastLine = new RawContextKey(TerminalContextKeyStrings.AccessibleBufferOnLastLine, false, localize('terminalAccessibleBufferOnLastLineContextKey', "Whether the accessible buffer focus is on the last line.")); + /** Whether a terminal in the editor area is focused. */ export const editorFocus = new RawContextKey(TerminalContextKeyStrings.EditorFocus, false, localize('terminalEditorFocusContextKey', "Whether a terminal in the editor area is focused.")); diff --git a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts index 75e281c9f2d..43b4589df6c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts @@ -22,6 +22,10 @@ export const terminalStrings = { value: localize('workbench.action.terminal.focus', "Focus Terminal"), original: 'Focus Terminal' }, + focusAndHideAccessibleBuffer: { + value: localize('workbench.action.terminal.focusAndHideAccessibleBuffer', "Focus Terminal and Hide Accessible Buffer"), + original: 'Focus Terminal and Hide Accessible Buffer' + }, kill: { value: localize('killTerminal', "Kill Terminal"), original: 'Kill Terminal', diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index b69943c18de..6fe33fdff35 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -11,7 +11,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { terminalTabFocusModeContextKey } from 'vs/platform/terminal/common/terminal'; +import { TerminalLocation, terminalTabFocusModeContextKey } from 'vs/platform/terminal/common/terminal'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -20,6 +20,7 @@ import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/brow import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { TerminalAccessibleContentProvider } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp'; import { AccessibleBufferWidget, NavigationType } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer'; import { TextAreaSyncAddon } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon'; @@ -47,7 +48,7 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr } registerTerminalContribution(TextAreaSyncContribution.ID, TextAreaSyncContribution); -class AccessibleBufferContribution extends DisposableStore implements ITerminalContribution { +export class AccessibleBufferContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.accessible-buffer'; private _xterm: IXtermTerminal & { raw: Terminal } | undefined; static get(instance: ITerminalInstance): AccessibleBufferContribution | null { @@ -83,6 +84,9 @@ class AccessibleBufferContribution extends DisposableStore implements ITerminalC navigateToCommand(type: NavigationType): void { return this._accessibleBufferWidget?.navigateToCommand(type); } + hide(): void { + this._accessibleBufferWidget?.hide(); + } } registerTerminalContribution(AccessibleBufferContribution.ID, AccessibleBufferContribution); @@ -114,6 +118,7 @@ registerTerminalAction({ keybinding: [ { primary: KeyMod.Shift | KeyCode.Tab, + secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.Alt | KeyCode.F2], weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.focus, ContextKeyExpr.or(terminalTabFocusModeContextKey, TerminalContextKeys.accessibleBufferFocus.negate())) } @@ -204,3 +209,26 @@ registerTerminalAction({ await AccessibleBufferContribution.get(instance)?.navigateToCommand(NavigationType.Previous); } }); + +registerTerminalAction({ + id: TerminalCommandId.FocusAndHideAccessibleBuffer, + title: terminalStrings.focusAndHideAccessibleBuffer, + f1: false, + keybinding: { + when: ContextKeyExpr.and(TerminalContextKeys.accessibleBufferFocus, TerminalContextKeys.accessibleBufferOnLastLine), + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, + weight: KeybindingWeight.WorkbenchContrib + }, + precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), + run: async (c) => { + const instance = c.service.activeInstance || await c.service.createTerminal({ location: TerminalLocation.Panel }); + if (!instance) { + return; + } + const contribution = instance.getContribution('terminal.accessible-buffer'); + if (contribution) { + contribution.hide(); + } + instance.focus(true); + } +}); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts index fab258f94e6..bca9d64d23a 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts @@ -56,7 +56,7 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { @ITerminalLogService private readonly _logService: ITerminalLogService, @ITerminalService _terminalService: ITerminalService ) { - super(ClassName.AccessibleBuffer, _instance, _xterm, TerminalContextKeys.accessibleBufferFocus, _instantiationService, _modelService, _configurationService, _contextKeyService, _terminalService); + super(ClassName.AccessibleBuffer, _instance, _xterm, TerminalContextKeys.accessibleBufferFocus, TerminalContextKeys.accessibleBufferOnLastLine, _instantiationService, _modelService, _configurationService, _contextKeyService, _terminalService); this._bufferTracker = _instantiationService.createInstance(BufferContentTracker, _xterm); this.element.ariaRoleDescription = localize('terminal.integrated.accessibleBuffer', 'Terminal buffer'); this.updateEditor(); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts index 46a475b67f3..77fd6173934 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts @@ -41,6 +41,7 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { protected _listeners: IDisposable[] = []; private readonly _focusedContextKey?: IContextKey; + private readonly _focusedLastLineContextKey?: IContextKey; private readonly _focusTracker?: dom.IFocusTracker; constructor( @@ -48,6 +49,7 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { protected readonly _instance: Pick, protected readonly _xterm: Pick & { raw: Terminal }, private _focusContextKey: RawContextKey | undefined, + private _focusLastLineContextKey: RawContextKey | undefined, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IModelService private readonly _modelService: IModelService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -87,11 +89,22 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { this._element.replaceChildren(this._editorContainer); this._xtermElement.insertAdjacentElement('beforebegin', this._element); - if (this._focusContextKey) { + if (this._focusContextKey && this._focusLastLineContextKey) { this._focusTracker = this.add(dom.trackFocus(this._editorContainer)); this._focusedContextKey = this._focusContextKey.bindTo(this._contextKeyService); - this.add(this._focusTracker.onDidFocus(() => this._focusedContextKey?.set(true))); - this.add(this._focusTracker.onDidBlur(() => this._focusedContextKey?.reset())); + this._focusedLastLineContextKey = this._focusLastLineContextKey.bindTo(this._contextKeyService); + this.add(this._focusTracker.onDidFocus(() => { + this._focusedContextKey?.set(true); + this._focusedLastLineContextKey?.set(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); + })); + this.add(this._focusTracker.onDidBlur(() => { + this._focusedContextKey?.reset(); + this._focusedLastLineContextKey?.reset(); + })); + this._editorWidget.onDidChangeCursorPosition(() => { + console.log(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); + this._focusedLastLineContextKey?.set(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); + }); } this.add(Event.runAndSubscribe(this._xterm.raw.onResize, () => this.layout())); From 30907cfef18c34f8d799910712a6194bae8b9a48 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 23 Aug 2023 19:31:51 +0200 Subject: [PATCH 090/607] TreeItem.label ignored by resourceUri for untitled editor (#191103) Fixes #189505 --- src/vs/workbench/browser/parts/views/treeView.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index aedd476a89d..c5dc8bf57e3 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -1217,7 +1217,8 @@ class TreeRenderer extends Disposable implements ITreeRenderer Date: Wed, 23 Aug 2023 10:32:56 -0700 Subject: [PATCH 091/607] fix #189358 --- .../browser/terminal.accessibility.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 6fe33fdff35..61d6cc20f6b 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -166,8 +166,7 @@ registerTerminalAction({ weight: KeybindingWeight.WorkbenchContrib + 2 }, { - primary: KeyMod.CtrlCmd | KeyCode.DownArrow, - mac: { primary: KeyMod.Alt | KeyCode.DownArrow }, + primary: KeyMod.Alt | KeyCode.DownArrow, when: ContextKeyExpr.and(TerminalContextKeys.accessibleBufferFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), weight: KeybindingWeight.WorkbenchContrib + 2 } From cbc3479604b22f3c573932a46766d43563bb2f06 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 10:41:27 -0700 Subject: [PATCH 092/607] Revert "Merge pull request #190776 from microsoft/merogge/delay" This reverts commit 990da210e0e4fc528570ab83d068259766ff4e49, reversing changes made to 1f76cf794b240be23d6135831ed41b160991804f. --- .../accessibility/browser/accessibilityContributions.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 80cdebaab8e..3d6968b88c4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -282,7 +282,6 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { this._register(AccessibleViewAction.addImplementation(95, 'inline-completions', accessor => { const accessibleViewService = accessor.get(IAccessibleViewService); const codeEditorService = accessor.get(ICodeEditorService); - const contextViewService = accessor.get(IContextViewService); const show = () => { const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); if (!editor) { @@ -311,12 +310,10 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { editor.focus(); }, next() { - contextViewService.hideContextView(); - setTimeout(() => model.next().then(() => show()), 50); + model.next().then(() => show()); }, previous() { - contextViewService.hideContextView(); - setTimeout(() => model.previous().then(() => show()), 50); + model.previous().then(() => show()); }, options: this._options }); From 2ab4a3f547d378d5df7530922bd001376e714795 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 23 Aug 2023 10:41:33 -0700 Subject: [PATCH 093/607] tunnels: implement default port forwarding for managed RA's (#191099) * tunnels: implement default port forwarding for managed RA's The default implementation of port forwarding works from the shared process to traditional addressed remote authorities. However, this didn't work with managed remote authorities. Rather than implementing a chain of message passing from the shared process back up to the extension host, this PR reuses the `NodeRemoteTunnel` to provide default forwarding logic in the Node ext host. It also makes the `ManagedSocket` more generic for reuse there. Fixes #190859 * fix * address comments * address comments, fix build * rm import --- .../platform/remote/common/managedSocket.ts | 119 ++++++++++++++++++ src/vs/platform/tunnel/node/tunnelService.ts | 50 +++++--- .../api/browser/mainThreadManagedSockets.ts | 118 +++-------------- .../api/common/extHostExtensionService.ts | 6 +- .../api/common/extHostTunnelService.ts | 21 +++- .../api/node/extHost.node.services.ts | 3 + .../api/node/extHostTunnelService.ts | 112 ++++++++++++++++- .../browser/mainThreadManagedSockets.test.ts | 7 +- 8 files changed, 311 insertions(+), 125 deletions(-) diff --git a/src/vs/platform/remote/common/managedSocket.ts b/src/vs/platform/remote/common/managedSocket.ts index 9cbf5f326e8..0f55617264d 100644 --- a/src/vs/platform/remote/common/managedSocket.ts +++ b/src/vs/platform/remote/common/managedSocket.ts @@ -4,6 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer, encodeBase64 } from 'vs/base/common/buffer'; +import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { ISocket, SocketCloseEvent, SocketDiagnostics, SocketDiagnosticsEventType } from 'vs/base/parts/ipc/common/ipc.net'; export const makeRawSocketHeaders = (path: string, query: string, deubgLabel: string) => { // https://tools.ietf.org/html/rfc6455#section-4 @@ -24,3 +27,119 @@ export const makeRawSocketHeaders = (path: string, query: string, deubgLabel: st }; export const socketRawEndHeaderSequence = VSBuffer.fromString('\r\n\r\n'); + +export interface RemoteSocketHalf { + onData: Emitter; + onClose: Emitter; + onEnd: Emitter; +} + +/** Should be called immediately after making a ManagedSocket to make it ready for data flow. */ +export async function connectManagedSocket( + socket: T, + path: string, query: string, debugLabel: string, + half: RemoteSocketHalf +): Promise { + socket.write(VSBuffer.fromString(makeRawSocketHeaders(path, query, debugLabel))); + + const d = new DisposableStore(); + try { + return await new Promise((resolve, reject) => { + let dataSoFar: VSBuffer | undefined; + d.add(socket.onData(d_1 => { + if (!dataSoFar) { + dataSoFar = d_1; + } else { + dataSoFar = VSBuffer.concat([dataSoFar, d_1], dataSoFar.byteLength + d_1.byteLength); + } + + const index = dataSoFar.indexOf(socketRawEndHeaderSequence); + if (index === -1) { + return; + } + + resolve(socket); + // pause data events until the socket consumer is hooked up. We may + // immediately emit remaining data, but if not there may still be + // microtasks queued which would fire data into the abyss. + socket.pauseData(); + + const rest = dataSoFar.slice(index + socketRawEndHeaderSequence.byteLength); + if (rest.byteLength) { + half.onData.fire(rest); + } + })); + + d.add(socket.onClose(err => reject(err ?? new Error('socket closed')))); + d.add(socket.onEnd(() => reject(new Error('socket ended')))); + }); + } catch (e) { + socket.dispose(); + throw e; + } finally { + d.dispose(); + } +} + +export abstract class ManagedSocket extends Disposable implements ISocket { + private readonly pausableDataEmitter = this._register(new PauseableEmitter()); + + public onData: Event = (...args) => { + if (this.pausableDataEmitter.isPaused) { + queueMicrotask(() => this.pausableDataEmitter.resume()); + } + return this.pausableDataEmitter.event(...args); + }; + public onClose: Event; + public onEnd: Event; + + private readonly didDisposeEmitter = this._register(new Emitter()); + public onDidDispose = this.didDisposeEmitter.event; + + private ended = false; + + protected constructor( + private readonly debugLabel: string, + half: RemoteSocketHalf, + ) { + super(); + + this._register(half.onData); + this._register(half.onData.event(data => this.pausableDataEmitter.fire(data))); + + this.onClose = this._register(half.onClose).event; + this.onEnd = this._register(half.onEnd).event; + } + + /** Pauses data events until a new listener comes in onData() */ + public pauseData() { + this.pausableDataEmitter.pause(); + } + + /** Flushes data to the socket. */ + public drain(): Promise { + return Promise.resolve(); + } + + /** Ends the remote socket. */ + public end(): void { + this.ended = true; + this.closeRemote(); + } + + public abstract write(buffer: VSBuffer): void; + protected abstract closeRemote(): void; + + traceSocketEvent(type: SocketDiagnosticsEventType, data?: any): void { + SocketDiagnostics.traceSocketEvent(this, this.debugLabel, type, data); + } + + override dispose(): void { + if (!this.ended) { + this.closeRemote(); + } + + this.didDisposeEmitter.fire(); + super.dispose(); + } +} diff --git a/src/vs/platform/tunnel/node/tunnelService.ts b/src/vs/platform/tunnel/node/tunnelService.ts index 50eb82abc61..0894400360d 100644 --- a/src/vs/platform/tunnel/node/tunnelService.ts +++ b/src/vs/platform/tunnel/node/tunnelService.ts @@ -10,14 +10,15 @@ import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { Barrier } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; +import { OS } from 'vs/base/common/platform'; +import { ISocket } from 'vs/base/parts/ipc/common/ipc.net'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; -import { connectRemoteAgentTunnel, IAddressProvider, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection'; -import { AbstractTunnelService, isAllInterfaces, ISharedTunnelsService as ISharedTunnelsService, isLocalhost, isPortPrivileged, isTunnelProvider, ITunnelProvider, ITunnelService, RemoteTunnel, TunnelPrivacyId } from 'vs/platform/tunnel/common/tunnel'; -import { ISignService } from 'vs/platform/sign/common/sign'; -import { OS } from 'vs/base/common/platform'; +import { IAddressProvider, IConnectionOptions, connectRemoteAgentTunnel } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { AbstractTunnelService, ISharedTunnelsService, ITunnelProvider, ITunnelService, RemoteTunnel, TunnelPrivacyId, isAllInterfaces, isLocalhost, isPortPrivileged, isTunnelProvider } from 'vs/platform/tunnel/common/tunnel'; async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise { let readyTunnel: NodeRemoteTunnel | undefined; @@ -32,7 +33,7 @@ async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost return readyTunnel!; } -class NodeRemoteTunnel extends Disposable implements RemoteTunnel { +export class NodeRemoteTunnel extends Disposable implements RemoteTunnel { public readonly tunnelRemotePort: number; public tunnelLocalPort!: number; @@ -113,7 +114,7 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel { const tunnelRemoteHost = (isLocalhost(this.tunnelRemoteHost) || isAllInterfaces(this.tunnelRemoteHost)) ? 'localhost' : this.tunnelRemoteHost; const protocol = await connectRemoteAgentTunnel(this._options, tunnelRemoteHost, this.tunnelRemotePort); - const remoteSocket = (protocol.getSocket()).socket; + const remoteSocket = protocol.getSocket(); const dataChunk = protocol.readEntireBuffer(); protocol.dispose(); @@ -132,17 +133,19 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel { if (localSocket.localAddress) { this._socketsDispose.delete(localSocket.localAddress); } - remoteSocket.destroy(); + if (remoteSocket instanceof NodeSocket) { + remoteSocket.socket.destroy(); + } else { + remoteSocket.end(); + } }); - remoteSocket.on('end', () => localSocket.end()); - remoteSocket.on('close', () => localSocket.end()); - remoteSocket.on('error', () => { - localSocket.destroy(); - }); + if (remoteSocket instanceof NodeSocket) { + this._mirrorNodeSocket(localSocket, remoteSocket); + } else { + this._mirrorGenericSocket(localSocket, remoteSocket); + } - localSocket.pipe(remoteSocket); - remoteSocket.pipe(localSocket); if (localSocket.localAddress) { this._socketsDispose.set(localSocket.localAddress, () => { // Need to end instead of unpipe, otherwise whatever is connected locally could end up "stuck" with whatever state it had until manually exited. @@ -151,6 +154,25 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel { }); } } + + private _mirrorGenericSocket(localSocket: net.Socket, remoteSocket: ISocket) { + remoteSocket.onClose(() => localSocket.destroy()); + remoteSocket.onEnd(() => localSocket.end()); + remoteSocket.onData(d => localSocket.write(d.buffer)); + localSocket.resume(); + } + + private _mirrorNodeSocket(localSocket: net.Socket, remoteNodeSocket: NodeSocket) { + const remoteSocket = remoteNodeSocket.socket; + remoteSocket.on('end', () => localSocket.end()); + remoteSocket.on('close', () => localSocket.end()); + remoteSocket.on('error', () => { + localSocket.destroy(); + }); + + remoteSocket.pipe(localSocket); + localSocket.pipe(remoteSocket); + } } export class BaseTunnelService extends AbstractTunnelService { diff --git a/src/vs/workbench/api/browser/mainThreadManagedSockets.ts b/src/vs/workbench/api/browser/mainThreadManagedSockets.ts index dbd5c34ac3e..5cae557028c 100644 --- a/src/vs/workbench/api/browser/mainThreadManagedSockets.ts +++ b/src/vs/workbench/api/browser/mainThreadManagedSockets.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MainContext, ExtHostContext, MainThreadManagedSocketsShape, ExtHostManagedSocketsShape } from 'vs/workbench/api/common/extHost.protocol'; -import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { ManagedRemoteConnection, RemoteConnectionType } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { VSBuffer } from 'vs/base/common/buffer'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { ISocket, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net'; +import { ManagedSocket, RemoteSocketHalf, connectManagedSocket } from 'vs/platform/remote/common/managedSocket'; +import { ManagedRemoteConnection, RemoteConnectionType } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRemoteSocketFactoryService, ISocketFactory } from 'vs/platform/remote/common/remoteSocketFactoryService'; -import { ISocket, SocketCloseEvent, SocketCloseEventType, SocketDiagnostics, SocketDiagnosticsEventType } from 'vs/base/parts/ipc/common/ipc.net'; -import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; -import { makeRawSocketHeaders, socketRawEndHeaderSequence } from 'vs/platform/remote/common/managedSocket'; +import { ExtHostContext, ExtHostManagedSocketsShape, MainContext, MainThreadManagedSocketsShape } from 'vs/workbench/api/common/extHost.protocol'; +import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; @extHostNamedCustomer(MainContext.MainThreadManagedSockets) export class MainThreadManagedSockets extends Disposable implements MainThreadManagedSocketsShape { @@ -51,7 +51,7 @@ export class MainThreadManagedSockets extends Disposable implements MainThreadMa }; that._remoteSockets.set(socketId, half); - ManagedSocket.connect(socketId, that._proxy, path, query, debugLabel, half) + MainThreadManagedSocket.connect(socketId, that._proxy, path, query, debugLabel, half) .then( socket => { socket.onDidDispose(() => that._remoteSockets.delete(socketId)); @@ -91,117 +91,35 @@ export class MainThreadManagedSockets extends Disposable implements MainThreadMa } } -export interface RemoteSocketHalf { - onData: Emitter; - onClose: Emitter; - onEnd: Emitter; -} - -export class ManagedSocket extends Disposable implements ISocket { +export class MainThreadManagedSocket extends ManagedSocket { public static connect( socketId: number, proxy: ExtHostManagedSocketsShape, path: string, query: string, debugLabel: string, - half: RemoteSocketHalf - ): Promise { - const socket = new ManagedSocket(socketId, proxy, debugLabel, half.onClose, half.onData, half.onEnd); - - socket.write(VSBuffer.fromString(makeRawSocketHeaders(path, query, debugLabel))); - - const d = new DisposableStore(); - return new Promise((resolve, reject) => { - let dataSoFar: VSBuffer | undefined; - d.add(socket.onData(d => { - if (!dataSoFar) { - dataSoFar = d; - } else { - dataSoFar = VSBuffer.concat([dataSoFar, d], dataSoFar.byteLength + d.byteLength); - } - - const index = dataSoFar.indexOf(socketRawEndHeaderSequence); - if (index === -1) { - return; - } - - resolve(socket); - // pause data events until the socket consumer is hooked up. We may - // immediately emit remaining data, but if not there may still be - // microtasks queued which would fire data into the abyss. - socket.pauseData(); - - const rest = dataSoFar.slice(index + socketRawEndHeaderSequence.byteLength); - if (rest.byteLength) { - half.onData.fire(rest); - } - })); - - d.add(socket.onClose(err => reject(err ?? new Error('socket closed')))); - d.add(socket.onEnd(() => reject(new Error('socket ended')))); - }).finally(() => d.dispose()); + ): Promise { + const socket = new MainThreadManagedSocket(socketId, proxy, debugLabel, half); + return connectManagedSocket(socket, path, query, debugLabel, half); } - private readonly pausableDataEmitter = this._register(new PauseableEmitter()); - - public onData: Event = (...args) => { - if (this.pausableDataEmitter.isPaused) { - queueMicrotask(() => this.pausableDataEmitter.resume()); - } - return this.pausableDataEmitter.event(...args); - }; - public onClose: Event; - public onEnd: Event; - - private readonly didDisposeEmitter = this._register(new Emitter()); - public onDidDispose = this.didDisposeEmitter.event; - - private ended = false; - private constructor( private readonly socketId: number, private readonly proxy: ExtHostManagedSocketsShape, - private readonly debugLabel: string, - onCloseEmitter: Emitter, - onDataEmitter: Emitter, - onEndEmitter: Emitter, + debugLabel: string, + half: RemoteSocketHalf, ) { - super(); - - this._register(onDataEmitter); - this._register(onDataEmitter.event(data => this.pausableDataEmitter.fire(data))); - - this.onClose = this._register(onCloseEmitter).event; - this.onEnd = this._register(onEndEmitter).event; + super(debugLabel, half); } - /** Pauses data events until a new listener comes in onData() */ - pauseData() { - this.pausableDataEmitter.pause(); - } - - write(buffer: VSBuffer): void { + public override write(buffer: VSBuffer): void { this.proxy.$remoteSocketWrite(this.socketId, buffer); } - end(): void { - this.ended = true; + protected override closeRemote(): void { this.proxy.$remoteSocketEnd(this.socketId); } - drain(): Promise { + public override drain(): Promise { return this.proxy.$remoteSocketDrain(this.socketId); } - - traceSocketEvent(type: SocketDiagnosticsEventType, data?: any): void { - SocketDiagnostics.traceSocketEvent(this, this.debugLabel, type, data); - } - - override dispose(): void { - if (!this.ended) { - this.proxy.$remoteSocketEnd(this.socketId); - } - - this.didDisposeEmitter.fire(); - super.dispose(); - } } diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 0c35ea254f3..0a0eebdd8a5 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -861,9 +861,11 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme performance.mark(`code/extHost/willResolveAuthority/${authorityPrefix}`); result = await resolver.resolve(remoteAuthority, { resolveAttempt, execServer }); performance.mark(`code/extHost/didResolveAuthorityOK/${authorityPrefix}`); - // todo@connor4312: we probably need to chain tunnels too, how does this work with 'public' tunnels? logInfo(`setting tunnel factory...`); - this._register(await this._extHostTunnelService.setTunnelFactory(resolver)); + this._register(await this._extHostTunnelService.setTunnelFactory( + resolver, + ExtHostManagedResolvedAuthority.isManagedResolvedAuthority(result) ? result : undefined + )); } else { logInfo(`invoking resolveExecServer() for ${remoteAuthority}`); performance.mark(`code/extHost/willResolveExecServer/${authorityPrefix}`); diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts index 34fed87a677..7040aca80d9 100644 --- a/src/vs/workbench/api/common/extHostTunnelService.ts +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -54,7 +54,7 @@ export interface IExtHostTunnelService extends ExtHostTunnelServiceShape { openTunnel(extension: IExtensionDescription, forward: TunnelOptions): Promise; getTunnels(): Promise; onDidChangeTunnels: vscode.Event; - setTunnelFactory(provider: vscode.RemoteAuthorityResolver | undefined): Promise; + setTunnelFactory(provider: vscode.RemoteAuthorityResolver | undefined, managedRemoteAuthority: vscode.ManagedResolvedAuthority | undefined): Promise; registerPortsAttributesProvider(portSelector: PortAttributesSelector, provider: vscode.PortAttributesProvider): IDisposable; registerTunnelProvider(provider: vscode.TunnelProvider, information: vscode.TunnelInformation): Promise; } @@ -165,7 +165,15 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe })); } - async setTunnelFactory(provider: vscode.RemoteAuthorityResolver | undefined): Promise { + /** + * Applies the tunnel metadata and factory found in the remote authority + * resolver to the tunnel system. + * + * `managedRemoteAuthority` should be be passed if the resolver returned on. + * If this is the case, the tunnel cannot be connected to via a websocket from + * the share process, so a synethic tunnel factory is used as a default. + */ + async setTunnelFactory(provider: vscode.RemoteAuthorityResolver | undefined, managedRemoteAuthority: vscode.ManagedResolvedAuthority | undefined): Promise { // Do not wait for any of the proxy promises here. // It will delay startup and there is nothing that needs to be waited for. if (provider) { @@ -176,8 +184,9 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe this._showCandidatePort = provider.showCandidatePort; this._proxy.$setCandidateFilter(); } - if (provider.tunnelFactory) { - this._forwardPortProvider = provider.tunnelFactory; + const tunnelFactory = provider.tunnelFactory ?? (managedRemoteAuthority ? this.makeManagedTunnelFactory(managedRemoteAuthority) : undefined); + if (tunnelFactory) { + this._forwardPortProvider = tunnelFactory; let privacyOptions = provider.tunnelFeatures?.privacyOptions ?? []; if (provider.tunnelFeatures?.public && (privacyOptions.length === 0)) { privacyOptions = [ @@ -210,6 +219,10 @@ export class ExtHostTunnelService extends Disposable implements IExtHostTunnelSe }); } + protected makeManagedTunnelFactory(_authority: vscode.ManagedResolvedAuthority): vscode.RemoteAuthorityResolver['tunnelFactory'] { + return undefined; // may be overridden + } + async $closeTunnel(remote: { host: string; port: number }, silent?: boolean): Promise { if (this._extensionTunnels.has(remote.host)) { const hostMap = this._extensionTunnels.get(remote.host)!; diff --git a/src/vs/workbench/api/node/extHost.node.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts index 9a4ffa8b033..582cea087ee 100644 --- a/src/vs/workbench/api/node/extHost.node.services.ts +++ b/src/vs/workbench/api/node/extHost.node.services.ts @@ -24,6 +24,8 @@ import { NodeExtHostVariableResolverProviderService } from 'vs/workbench/api/nod import { IExtHostVariableResolverProvider } from 'vs/workbench/api/common/extHostVariableResolverService'; import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ISignService } from 'vs/platform/sign/common/sign'; +import { SignService } from 'vs/platform/sign/node/signService'; // ######################################################################### // ### ### @@ -34,6 +36,7 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; registerSingleton(IExtHostExtensionService, ExtHostExtensionService, InstantiationType.Eager); registerSingleton(ILoggerService, ExtHostLoggerService, InstantiationType.Delayed); registerSingleton(ILogService, new SyncDescriptor(ExtHostLogService, [false], true)); +registerSingleton(ISignService, SignService, InstantiationType.Delayed); registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths, InstantiationType.Eager); registerSingleton(IExtHostDebugService, ExtHostDebugService, InstantiationType.Eager); diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index 56e0de8c855..561f731654d 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -4,17 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import { exec } from 'child_process'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { Emitter } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { MovingAverage } from 'vs/base/common/numbers'; import { isLinux } from 'vs/base/common/platform'; import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; +import { ISocket, SocketCloseEventType } from 'vs/base/parts/ipc/common/ipc.net'; import { ILogService } from 'vs/platform/log/common/log'; +import { ManagedSocket, RemoteSocketHalf, connectManagedSocket } from 'vs/platform/remote/common/managedSocket'; +import { ManagedRemoteConnection } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { ISignService } from 'vs/platform/sign/common/sign'; import { isAllInterfaces, isLocalhost } from 'vs/platform/tunnel/common/tunnel'; +import { NodeRemoteTunnel } from 'vs/platform/tunnel/node/tunnelService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; import { CandidatePort } from 'vs/workbench/services/remote/common/tunnelModel'; +import * as vscode from 'vscode'; export function getSockets(stdout: string): Record { const lines = stdout.trim().split('\n'); @@ -171,8 +180,9 @@ export class NodeExtHostTunnelService extends ExtHostTunnelService { constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, - @IExtHostInitDataService initData: IExtHostInitDataService, - @ILogService logService: ILogService + @IExtHostInitDataService private readonly initData: IExtHostInitDataService, + @ILogService logService: ILogService, + @ISignService private readonly signService: ISignService, ) { super(extHostRpc, initData, logService); if (isLinux && initData.remote.isRemote && initData.remote.authority) { @@ -297,4 +307,102 @@ export class NodeExtHostTunnelService extends ExtHostTunnelService { } }); } + + protected override makeManagedTunnelFactory(authority: vscode.ManagedResolvedAuthority): vscode.RemoteAuthorityResolver['tunnelFactory'] { + return async (tunnelOptions) => { + const t = new NodeRemoteTunnel( + { + commit: this.initData.commit, + quality: this.initData.quality, + logService: this.logService, + ipcLogger: null, + // services and address providers have stubs since we don't need + // the connection identification that the renderer process uses + remoteSocketFactoryService: { + _serviceBrand: undefined, + async connect(_connectTo: ManagedRemoteConnection, path: string, query: string, debugLabel: string): Promise { + const result = await authority.makeConnection(); + return ExtHostManagedSocket.connect(result, path, query, debugLabel); + }, + register() { + throw new Error('not implemented'); + }, + }, + addressProvider: { + getAddress() { + return Promise.resolve({ + connectTo: new ManagedRemoteConnection(0), + connectionToken: authority.connectionToken, + }); + }, + }, + signService: this.signService, + }, + 'localhost', + tunnelOptions.remoteAddress.host || 'localhost', + tunnelOptions.remoteAddress.port, + tunnelOptions.localAddressPort, + ); + + await t.waitForReady(); + + const disposeEmitter = new Emitter(); + + return { + localAddress: t.localAddress, + remoteAddress: { port: t.tunnelRemotePort, host: t.tunnelRemoteHost }, + onDidDispose: disposeEmitter.event, + dispose: () => { + t.dispose(); + disposeEmitter.fire(); + disposeEmitter.dispose(); + }, + }; + }; + } +} + +class ExtHostManagedSocket extends ManagedSocket { + public static connect( + passing: vscode.ManagedMessagePassing, + path: string, query: string, debugLabel: string, + ): Promise { + const d = new DisposableStore(); + const half: RemoteSocketHalf = { + onClose: d.add(new Emitter()), + onData: d.add(new Emitter()), + onEnd: d.add(new Emitter()), + }; + + d.add(passing.onDidReceiveMessage(d => half.onData.fire(VSBuffer.wrap(d)))); + d.add(passing.onDidEnd(() => half.onEnd.fire())); + d.add(passing.onDidClose(error => half.onClose.fire({ + type: SocketCloseEventType.NodeSocketCloseEvent, + error, + hadError: !!error + }))); + + const socket = new ExtHostManagedSocket(passing, debugLabel, half); + socket._register(d); + return connectManagedSocket(socket, path, query, debugLabel, half); + } + + constructor( + private readonly passing: vscode.ManagedMessagePassing, + debugLabel: string, + half: RemoteSocketHalf, + ) { + super(debugLabel, half); + } + + public override write(buffer: VSBuffer): void { + this.passing.send(buffer.buffer); + } + protected override closeRemote(): void { + this.passing.end(); + } + + public override async drain(): Promise { + await this.passing.drain?.(); + } } diff --git a/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts b/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts index 611ec9727c0..6eb1c08ad65 100644 --- a/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts @@ -10,7 +10,8 @@ import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { SocketCloseEvent } from 'vs/base/parts/ipc/common/ipc.net'; import { mock } from 'vs/base/test/common/mock'; -import { ManagedSocket, RemoteSocketHalf } from 'vs/workbench/api/browser/mainThreadManagedSockets'; +import { RemoteSocketHalf } from 'vs/platform/remote/common/managedSocket'; +import { MainThreadManagedSocket } from 'vs/workbench/api/browser/mainThreadManagedSockets'; import { ExtHostManagedSocketsShape } from 'vs/workbench/api/common/extHost.protocol'; suite('MainThreadManagedSockets', () => { @@ -68,7 +69,7 @@ suite('MainThreadManagedSockets', () => { }); async function doConnect() { - const socket = ManagedSocket.connect(1, extHost, '/hello', 'world=true', '', half); + const socket = MainThreadManagedSocket.connect(1, extHost, '/hello', 'world=true', '', half); await extHost.expectEvent(evt => evt.data && evt.data.startsWith('GET ws://localhost/hello?world=true&skipWebSocketFrames=true HTTP/1.1\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Key:'), 'websocket open event'); half.onData.fire(VSBuffer.fromString('Opened successfully ;)\r\n\r\n')); return await socket; @@ -79,7 +80,7 @@ suite('MainThreadManagedSockets', () => { }); test('includes trailing connection data', async () => { - const socketProm = ManagedSocket.connect(1, extHost, '/hello', 'world=true', '', half); + const socketProm = MainThreadManagedSocket.connect(1, extHost, '/hello', 'world=true', '', half); await extHost.expectEvent(evt => evt.data && evt.data.includes('GET ws://localhost'), 'websocket open event'); half.onData.fire(VSBuffer.fromString('Opened successfully ;)\r\n\r\nSome trailing data')); const socket = await socketProm; From ccf71f1571c764f6144e89f17a599d179f37a3d0 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 10:43:20 -0700 Subject: [PATCH 094/607] try to fix timing issue --- .../accessibility/browser/accessibilityContributions.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 3d6968b88c4..1baf82143d4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -310,10 +310,12 @@ export class InlineCompletionsAccessibleViewContribution extends Disposable { editor.focus(); }, next() { - model.next().then(() => show()); + model.next(); + setTimeout(() => show(), 50); }, previous() { - model.previous().then(() => show()); + model.previous(); + setTimeout(() => show(), 50); }, options: this._options }); From c08c2a789bb3b943e338b5132a81494bb0fbc29a Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 23 Aug 2023 20:19:44 +0200 Subject: [PATCH 095/607] ignore unexpected dom nodes (#191113) Fixes #169854: ignore surprising dom nodes --- src/vs/editor/browser/controller/mouseTarget.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 0215f9031a6..b39b8f95ba6 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -198,6 +198,13 @@ class ElementPath { ); } + public static isChildOfOverflowGuard(path: Uint8Array): boolean { + return ( + path.length >= 1 + && path[0] === PartFingerprint.OverflowGuard + ); + } + public static isChildOfOverflowingContentWidgets(path: Uint8Array): boolean { return ( path.length >= 1 @@ -538,6 +545,11 @@ export class MouseTargetFactory { let result: IMouseTarget | null = null; + if (!ElementPath.isChildOfOverflowGuard(request.targetPath) && !ElementPath.isChildOfOverflowingContentWidgets(request.targetPath)) { + // We only render dom nodes inside the overflow guard or in the overflowing content widgets + result = result || request.fulfillUnknown(); + } + result = result || MouseTargetFactory._hitTestContentWidget(ctx, resolvedRequest); result = result || MouseTargetFactory._hitTestOverlayWidget(ctx, resolvedRequest); result = result || MouseTargetFactory._hitTestMinimap(ctx, resolvedRequest); From 82643e93f57fe752a587c0537fe4866e2d44eddf Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Wed, 23 Aug 2023 11:28:35 -0700 Subject: [PATCH 096/607] Changed action widget header ellipses (#191096) changed from ellipses to semi-colons --- .../contrib/codeAction/browser/codeActionMenu.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts index 0f645908a5f..fc33e04fdd0 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts @@ -22,13 +22,13 @@ interface ActionGroup { const uncategorizedCodeActionGroup = Object.freeze({ kind: CodeActionKind.Empty, title: localize('codeAction.widget.id.more', 'More Actions...') }); const codeActionGroups = Object.freeze([ - { kind: CodeActionKind.QuickFix, title: localize('codeAction.widget.id.quickfix', 'Quick Fix...') }, - { kind: CodeActionKind.RefactorExtract, title: localize('codeAction.widget.id.extract', 'Extract...'), icon: Codicon.wrench }, - { kind: CodeActionKind.RefactorInline, title: localize('codeAction.widget.id.inline', 'Inline...'), icon: Codicon.wrench }, - { kind: CodeActionKind.RefactorRewrite, title: localize('codeAction.widget.id.convert', 'Rewrite...'), icon: Codicon.wrench }, - { kind: CodeActionKind.RefactorMove, title: localize('codeAction.widget.id.move', 'Move...'), icon: Codicon.wrench }, - { kind: CodeActionKind.SurroundWith, title: localize('codeAction.widget.id.surround', 'Surround With...'), icon: Codicon.symbolSnippet }, - { kind: CodeActionKind.Source, title: localize('codeAction.widget.id.source', 'Source Action...'), icon: Codicon.symbolFile }, + { kind: CodeActionKind.QuickFix, title: localize('codeAction.widget.id.quickfix', 'Quick Fix:') }, + { kind: CodeActionKind.RefactorExtract, title: localize('codeAction.widget.id.extract', 'Extract:'), icon: Codicon.wrench }, + { kind: CodeActionKind.RefactorInline, title: localize('codeAction.widget.id.inline', 'Inline:'), icon: Codicon.wrench }, + { kind: CodeActionKind.RefactorRewrite, title: localize('codeAction.widget.id.convert', 'Rewrite:'), icon: Codicon.wrench }, + { kind: CodeActionKind.RefactorMove, title: localize('codeAction.widget.id.move', 'Move:'), icon: Codicon.wrench }, + { kind: CodeActionKind.SurroundWith, title: localize('codeAction.widget.id.surround', 'Surround With:'), icon: Codicon.symbolSnippet }, + { kind: CodeActionKind.Source, title: localize('codeAction.widget.id.source', 'Source Action:'), icon: Codicon.symbolFile }, uncategorizedCodeActionGroup, ]); From c9481326e71e5a842f1addf0cbd181cdb1749d11 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 23 Aug 2023 20:33:30 +0200 Subject: [PATCH 097/607] Use full line decorations for comment range (#191107) Fixes #188901 --- .../workbench/contrib/comments/browser/commentColors.ts | 2 -- .../comments/browser/commentThreadRangeDecorator.ts | 6 ++++-- .../workbench/contrib/comments/browser/media/review.css | 9 --------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentColors.ts b/src/vs/workbench/contrib/comments/browser/commentColors.ts index bfc70a8a247..a300a745a56 100644 --- a/src/vs/workbench/contrib/comments/browser/commentColors.ts +++ b/src/vs/workbench/contrib/comments/browser/commentColors.ts @@ -15,9 +15,7 @@ const unresolvedCommentViewIcon = registerColor('commentsView.unresolvedIcon', { const resolvedCommentBorder = registerColor('editorCommentsWidget.resolvedBorder', { dark: resolvedCommentViewIcon, light: resolvedCommentViewIcon, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('resolvedCommentBorder', 'Color of borders and arrow for resolved comments.')); const unresolvedCommentBorder = registerColor('editorCommentsWidget.unresolvedBorder', { dark: unresolvedCommentViewIcon, light: unresolvedCommentViewIcon, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentBorder', 'Color of borders and arrow for unresolved comments.')); export const commentThreadRangeBackground = registerColor('editorCommentsWidget.rangeBackground', { dark: transparent(unresolvedCommentBorder, .1), light: transparent(unresolvedCommentBorder, .1), hcDark: transparent(unresolvedCommentBorder, .1), hcLight: transparent(unresolvedCommentBorder, .1) }, nls.localize('commentThreadRangeBackground', 'Color of background for comment ranges.')); -export const commentThreadRangeBorder = registerColor('editorCommentsWidget.rangeBorder', { dark: transparent(unresolvedCommentBorder, .4), light: transparent(unresolvedCommentBorder, .4), hcDark: transparent(unresolvedCommentBorder, .4), hcLight: transparent(unresolvedCommentBorder, .4) }, nls.localize('commentThreadRangeBorder', 'Color of border for comment ranges.')); export const commentThreadRangeActiveBackground = registerColor('editorCommentsWidget.rangeActiveBackground', { dark: transparent(unresolvedCommentBorder, .1), light: transparent(unresolvedCommentBorder, .1), hcDark: transparent(unresolvedCommentBorder, .1), hcLight: transparent(unresolvedCommentBorder, .1) }, nls.localize('commentThreadActiveRangeBackground', 'Color of background for currently selected or hovered comment range.')); -export const commentThreadRangeActiveBorder = registerColor('editorCommentsWidget.rangeActiveBorder', { dark: transparent(unresolvedCommentBorder, .4), light: transparent(unresolvedCommentBorder, .4), hcDark: transparent(unresolvedCommentBorder, .4), hcLight: transparent(unresolvedCommentBorder, .2) }, nls.localize('commentThreadActiveRangeBorder', 'Color of border for currently selected or hovered comment range.')); const commentThreadStateBorderColors = new Map([ [languages.CommentThreadState.Unresolved, unresolvedCommentBorder], diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts index 0c9828613d2..182a5f3b397 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts @@ -44,7 +44,8 @@ export class CommentThreadRangeDecorator extends Disposable { description: CommentThreadRangeDecorator.description, isWholeLine: false, zIndex: 20, - className: 'comment-thread-range' + className: 'comment-thread-range', + shouldFillLineOnLineBreak: true }; this.decorationOptions = ModelDecorationOptions.createDynamic(decorationOptions); @@ -53,7 +54,8 @@ export class CommentThreadRangeDecorator extends Disposable { description: CommentThreadRangeDecorator.description, isWholeLine: false, zIndex: 20, - className: 'comment-thread-range-current' + className: 'comment-thread-range-current', + shouldFillLineOnLineBreak: true }; this.activeDecorationOptions = ModelDecorationOptions.createDynamic(activeDecorationOptions); diff --git a/src/vs/workbench/contrib/comments/browser/media/review.css b/src/vs/workbench/contrib/comments/browser/media/review.css index dc27b9a50ae..87c845189d8 100644 --- a/src/vs/workbench/contrib/comments/browser/media/review.css +++ b/src/vs/workbench/contrib/comments/browser/media/review.css @@ -487,21 +487,12 @@ div.preview.inline .monaco-editor .comment-range-glyph { background: var(--vscode-editorGutter-commentRangeForeground); } -.monaco-editor .comment-thread-range, -.monaco-editor .comment-thread-range-current { - border-width: 1px; - border-style: solid; - box-sizing: border-box; -} - .monaco-editor .comment-thread-range { background-color: var(--vscode-editorCommentsWidget-rangeBackground); - border-color: var(--vscode-editorCommentsWidget-rangeBorder); } .monaco-editor .comment-thread-range-current { background-color: var(--vscode-editorCommentsWidget-rangeActiveBackground); - border-color: var(--vscode-editorCommentsWidget-rangeActiveBorder); } .monaco-editor .margin-view-overlays .comment-range-glyph.line-hover, From 4e628263ac4b553c6c044bd30762886542a3164d Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 23 Aug 2023 11:37:09 -0700 Subject: [PATCH 098/607] Add quick search to command center (#191105) * Add quick search to command center Fixes #191088 * Adjust commandCenterOrder * Update src/vs/workbench/contrib/search/browser/search.contribution.ts Co-authored-by: Tyler James Leonhardt --------- Co-authored-by: Tyler James Leonhardt --- .../contrib/search/browser/search.contribution.ts | 10 ++++++++-- .../search/browser/searchActionsTextQuickAccess.ts | 4 ++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index a48332a48f2..2f9b6d54d73 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -128,8 +128,14 @@ quickAccessRegistry.registerQuickAccessProvider({ ctor: TextSearchQuickAccess, prefix: TEXT_SEARCH_QUICK_ACCESS_PREFIX, contextKey: 'inTextSearchPicker', - placeholder: nls.localize('textSearchPickerPlaceholder', "Search for text in your workspace files."), - helpEntries: [{ description: nls.localize('textSearchPickerHelp', "Show All Text Results (experimental)"), commandId: Constants.QuickTextSearchActionId }] + placeholder: nls.localize('textSearchPickerPlaceholder', "Search for text in your workspace files (experimental)."), + helpEntries: [ + { + description: nls.localize('textSearchPickerHelp', "Search for Text (Experimental)"), + commandId: Constants.QuickTextSearchActionId, + commandCenterOrder: 65, + } + ] }); // Configuration diff --git a/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts b/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts index ebf0558987d..3971c1f112c 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts @@ -18,8 +18,8 @@ registerAction2(class TextSearchQuickAccessAction extends Action2 { super({ id: Constants.QuickTextSearchActionId, title: { - value: nls.localize('quickTextSearch', "Quick Text Search"), - original: 'Quick Text Search' + value: nls.localize('quickTextSearch', "Quick Text Search (Experimental)"), + original: 'Quick Text Search (Experimental)' }, category, menu: [{ From a8c87ea06b83205ced3c733c64def35e562a089b Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 11:52:23 -0700 Subject: [PATCH 099/607] also go to previous --- .../browser/terminal.accessibility.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 61d6cc20f6b..4eb13c439a6 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -193,8 +193,7 @@ registerTerminalAction({ weight: KeybindingWeight.WorkbenchContrib + 2 }, { - primary: KeyMod.CtrlCmd | KeyCode.UpArrow, - mac: { primary: KeyMod.Alt | KeyCode.UpArrow }, + primary: KeyMod.Alt | KeyCode.UpArrow, when: ContextKeyExpr.and(TerminalContextKeys.accessibleBufferFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), weight: KeybindingWeight.WorkbenchContrib + 2 } From ca8d21f30bc6ba83f8d37fc1af851c5596b7ab41 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 11:56:49 -0700 Subject: [PATCH 100/607] add to commands to skip shell --- src/vs/workbench/contrib/terminal/common/terminal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 6402859667a..46692bccd1b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -570,6 +570,7 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TerminalCommandId.HideSuggestWidget, TerminalCommandId.FocusHover, AccessibilityCommandId.OpenAccessibilityHelp, + TerminalCommandId.FocusAndHideAccessibleBuffer, 'editor.action.toggleTabFocusMode', 'notifications.hideList', 'notifications.hideToasts', From 5113da8cc6110ea815a38238faf7ee5c82dae032 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 23 Aug 2023 21:03:15 +0200 Subject: [PATCH 101/607] voice - better similarity compare of transcription --- .../actions/chatVoiceInputActions.ts | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 2ebbbb79bf0..f4d874d252d 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -76,22 +76,23 @@ class ChatVoiceInputSession { this.chatVoiceInputInProgressKey.set(true); let lastText: string | undefined = undefined; - let lastTextEqualCount = 0; + let lastTextSimilarCount = 0; this.currentChatVoiceInputSession.add(onDidTranscribe(text => { if (text) { - if (lastText && equalsIgnoreCase(text, lastText)) { - lastTextEqualCount++; - - if (lastTextEqualCount >= 2) { - context.widget.acceptInput(); - } + if (lastText && this.isSimilarTranscription(text, lastText)) { + lastTextSimilarCount++; } else { - lastTextEqualCount = 0; + lastTextSimilarCount = 0; lastText = text; + } + if (lastTextSimilarCount >= 2) { + context.widget.acceptInput(); + } else { context.widget.updateInput(text); } + } })); @@ -100,6 +101,20 @@ class ChatVoiceInputSession { })); } + private isSimilarTranscription(textA: string, textB: string): boolean { + + // Attempt to compare the 2 strings in a way to see + // if they are similar or not. As such we: + // - ignore trailing punctuation + // - collapse all whitespace + // - compare case insensitive + + return equalsIgnoreCase( + textA.replace(/[.,;:!?]+$/, '').replace(/\s+/g, ''), + textB.replace(/[.,;:!?]+$/, '').replace(/\s+/g, '') + ); + } + stop(): void { if (!this.currentChatVoiceInputSession) { return; From 3129f818521df5fb7584516ae25628202eb0e48a Mon Sep 17 00:00:00 2001 From: Neelesh Bodas Date: Wed, 23 Aug 2023 12:38:24 -0700 Subject: [PATCH 102/607] Add empty alt tag to small thumbnail images of the extensions --- src/vs/workbench/contrib/extensions/browser/extensionsList.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 565f74c9aae..4e89ae5c8af 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -80,7 +80,7 @@ export class Renderer implements IPagedRenderer { const preReleaseWidget = this.instantiationService.createInstance(PreReleaseBookmarkWidget, append(root, $('.extension-bookmark-container'))); const element = append(root, $('.extension-list-item')); const iconContainer = append(element, $('.icon-container')); - const icon = append(iconContainer, $('img.icon')); + const icon = append(iconContainer, $('img.icon', { alt: '' })); const iconRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer, false); const extensionPackBadgeWidget = this.instantiationService.createInstance(ExtensionPackBadgeWidget, iconContainer); const details = append(element, $('.details')); From fe71c9a5eba39efa9bcc9c80fa8928e496842dbe Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 12:48:54 -0700 Subject: [PATCH 103/607] fix #188926 --- .../browser/terminalAccessibleBuffer.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts index fab258f94e6..3e1d2ad466d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts @@ -14,6 +14,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService, IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetectionCapability'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import { ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; @@ -91,9 +92,9 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { this._resetPosition(); } - private _getEditorLineForCommand(command: ITerminalCommand): number | undefined { - let line = command.marker?.line; - if (line === undefined || !command.command.length || line < 0) { + private _getEditorLineForCommand(command: ITerminalCommand | ICurrentPartialCommand): number | undefined { + let line = 'marker' in command ? command.marker?.line : 'commandStartMarker' in command ? command.commandStartMarker?.line : undefined; + if (line === undefined || line < 0) { return; } line = this._bufferTracker.bufferToEditorLineMapping.get(line); @@ -105,6 +106,7 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { private _getCommandsWithEditorLine(): ICommandWithEditorLine[] | undefined { const commands = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.commands; + const currentCommand = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.currentCommand; if (!commands?.length) { return; } @@ -116,6 +118,12 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { } result.push({ command, lineNumber }); } + if (currentCommand) { + const lineNumber = this._getEditorLineForCommand(currentCommand); + if (!!lineNumber) { + result.push({ command: currentCommand, lineNumber }); + } + } return result; } @@ -135,7 +143,7 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { { label: localize('terminal.integrated.symbolQuickPick.labelNoExitCode', '{0}', command.command), lineNumber, - exitCode: command.exitCode + exitCode: 'exitCode' in command ? command.exitCode : undefined }); } const quickPick = this._quickInputService.createQuickPick(); @@ -261,5 +269,5 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { } } -interface ICommandWithEditorLine { command: ITerminalCommand; lineNumber: number } +interface ICommandWithEditorLine { command: ITerminalCommand | ICurrentPartialCommand; lineNumber: number } From 330fb1f905f91294f9fe124bcba8a154ec96dc54 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 23 Aug 2023 13:37:12 -0700 Subject: [PATCH 104/607] Handle right arrow on quick search menu (#191115) Fixes #191110 --- .../quickTextSearch/textSearchQuickAccess.ts | 67 +++++++++++++------ .../contrib/search/browser/searchView.ts | 2 +- .../contrib/search/common/constants.ts | 2 +- 3 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 15a82a6cf6a..d17d209b9b1 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -7,22 +7,26 @@ import { IMatch } from 'vs/base/common/filters'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; import { ThemeIcon } from 'vs/base/common/themables'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchCompressibleObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; import { FastAndSlowPicks, IPickerQuickAccessItem, PickerQuickAccessProvider, Picks } from 'vs/platform/quickinput/browser/pickerQuickAccess'; -import { IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { IKeyMods, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; +import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/common/views'; import { searchDetailsIcon, searchOpenInFileIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; import { FileMatch, Match, MatchInNotebook, RenderableMatch, SearchModel, searchComparer } from 'vs/workbench/contrib/search/browser/searchModel'; import { SearchView, getEditorSelectionFromMatch } from 'vs/workbench/contrib/search/browser/searchView'; -import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; -import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchSearchConfiguration, getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/services/search/common/queryBuilder'; -import { IPatternInfo, ISearchConfigurationProperties, ITextQuery, VIEW_ID } from 'vs/workbench/services/search/common/search'; +import { IPatternInfo, ITextQuery, VIEW_ID } from 'vs/workbench/services/search/common/search'; export const TEXT_SEARCH_QUICK_ACCESS_PREFIX = '% '; @@ -46,8 +50,8 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider('search'); + private get configuration() { + const editorConfig = this._configurationService.getValue().workbench?.editor; + const searchConfig = this._configurationService.getValue().search; + const quickAccessConfig = this._configurationService.getValue().workbench.quickOpen; + + return { + openEditorPinned: !editorConfig?.enablePreviewFromQuickOpen || !editorConfig?.enablePreview, + preserveInput: quickAccessConfig.preserveInput, + maxResults: searchConfig.maxResults, + smartCase: searchConfig.smartCase, + }; } private doSearch(contentPattern: string, token: CancellationToken): { @@ -170,11 +183,7 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - await this._editorService.openEditor({ - resource: fileMatch.resource, - options - }, ACTIVE_GROUP); - }, + accept: async (keyMods, event) => { + await this.handleAccept(fileMatch, { + keyMods, + selection: getEditorSelectionFromMatch(element, this.searchModel), + preserveFocus: event.inBackground, + forcePinned: event.inBackground, + indexedCellOptions: element instanceof MatchInNotebook ? { index: element.cellIndex, selection: element.range() } : undefined + }); + } }); } } return picks; } + + private async handleAccept(fileMatch: FileMatch, options: { keyMods?: IKeyMods; selection?: ITextEditorSelection; preserveFocus?: boolean; range?: IRange; forcePinned?: boolean; forceOpenSideBySide?: boolean; indexedCellOptions?: { index: number; selection?: Range } }): Promise { + const editorOptions = { + preserveFocus: options.preserveFocus, + pinned: options.keyMods?.ctrlCmd || options.forcePinned || this.configuration.openEditorPinned, + selection: options.selection + }; + + // from https://github.com/microsoft/vscode/blob/f40dabca07a1622b2a0ae3ee741cfc94ab964bef/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts#L1037 + const targetGroup = options.keyMods?.alt || (this.configuration.openEditorPinned && options.keyMods?.ctrlCmd) || options.forceOpenSideBySide ? SIDE_GROUP : ACTIVE_GROUP; + + await this._editorService.openEditor({ + resource: fileMatch.resource, + options: editorOptions + }, targetGroup); + } + protected _getPicks(contentPattern: string, disposables: DisposableStore, token: CancellationToken): Picks | Promise | FastAndSlowPicks> | FastAndSlowPicks | null { const allMatches = this.doSearch(contentPattern, token); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 07576765667..4cdc0341049 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1894,7 +1894,7 @@ export class SearchView extends ViewPane { pinned, selection, revealIfVisible: true, - indexedCellOptions: element instanceof MatchInNotebook ? { cellIndex: element.cellIndex, selection: element.range } : undefined, + indexedCellOptions: element instanceof MatchInNotebook ? { index: element.cellIndex, selection: element.range() } : undefined, }; try { diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index d6e4fd9d7a9..1f98bc77b35 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -30,7 +30,7 @@ export const AddCursorsAtSearchResults = 'addCursorsAtSearchResults'; export const RevealInSideBarForSearchResults = 'search.action.revealInSideBar'; export const ReplaceInFilesActionId = 'workbench.action.replaceInFiles'; export const ShowAllSymbolsActionId = 'workbench.action.showAllSymbols'; -export const QuickTextSearchActionId = 'workbench.action.quickTextSearch'; +export const QuickTextSearchActionId = 'workbench.action.experimental.quickTextSearch'; export const CancelSearchActionId = 'search.action.cancel'; export const RefreshSearchResultsActionId = 'search.action.refreshSearchResults'; export const FocusNextSearchResultActionId = 'search.action.focusNextSearchResult'; From 9b9b9d57917db6495d9e49255682a03173933abe Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 13:41:33 -0700 Subject: [PATCH 105/607] use correct line number --- .../accessibility/browser/textAreaSyncAddon.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 666f27389d9..379d6965b0a 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -91,23 +91,22 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { return; } const buffer = this._terminal.buffer.active; - const line = buffer.getLine(buffer.cursorY)?.translateToString(true); + const lineNumber = currentCommand.commandStartMarker?.line; + if (!lineNumber) { + return; + } + const line = buffer.getLine(lineNumber)?.translateToString(true); if (!line) { this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); return; } - if (currentCommand.commandStartX !== undefined) { - // Left prompt + if (!!currentCommand.commandStartX) { this._currentCommand = line.substring(currentCommand.commandStartX); this._cursorX = buffer.cursorX - currentCommand.commandStartX; - } else if (currentCommand.commandRightPromptStartX !== undefined) { - // Right prompt - this._currentCommand = line.substring(0, currentCommand.commandRightPromptStartX); - this._cursorX = buffer.cursorX; } else { this._currentCommand = undefined; this._cursorX = undefined; - this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: neither commandStartX nor commandRightPromptStartX`); + this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no commandStartX`); } } } From f96904ece94d72dc9b42db69444fdcf84608d80a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 13:47:57 -0700 Subject: [PATCH 106/607] don't use messy inline --- .../accessibility/browser/terminalAccessibleBuffer.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts index 3e1d2ad466d..0835367c86e 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts @@ -93,7 +93,12 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { } private _getEditorLineForCommand(command: ITerminalCommand | ICurrentPartialCommand): number | undefined { - let line = 'marker' in command ? command.marker?.line : 'commandStartMarker' in command ? command.commandStartMarker?.line : undefined; + let line: number | undefined; + if ('marker' in command) { + line = command.marker?.line; + } else if ('commandStartMarker' in command) { + line = command.commandStartMarker?.line; + } if (line === undefined || line < 0) { return; } From eca80ba5f50d404a09e7b02c1bbf24bb94211069 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 23 Aug 2023 13:49:01 -0700 Subject: [PATCH 107/607] better names --- .../accessibility/browser/textAreaSyncAddon.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 379d6965b0a..9916a6f60d0 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -95,13 +95,13 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { if (!lineNumber) { return; } - const line = buffer.getLine(lineNumber)?.translateToString(true); - if (!line) { + const commandLine = buffer.getLine(lineNumber)?.translateToString(true); + if (!commandLine) { this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); return; } if (!!currentCommand.commandStartX) { - this._currentCommand = line.substring(currentCommand.commandStartX); + this._currentCommand = commandLine.substring(currentCommand.commandStartX); this._cursorX = buffer.cursorX - currentCommand.commandStartX; } else { this._currentCommand = undefined; From 557695b920d4779d63e80d9d1597dc57d9b2a7c2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Aug 2023 14:10:55 -0700 Subject: [PATCH 108/607] Fix inlay hint location (#191122) --- .../src/languageFeatures/inlayHints.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts index 5363619579b..1b9d554688d 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts @@ -77,7 +77,7 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin return response.body.map(hint => { const result = new vscode.InlayHint( Position.fromLocation(hint.position), - this.convertInlayHintText(model.uri, hint), + this.convertInlayHintText(hint), hint.kind && fromProtocolInlayHintKind(hint.kind) ); result.paddingLeft = hint.whitespaceBefore; @@ -86,19 +86,18 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin }); } - private convertInlayHintText(resource: vscode.Uri, tsHint: Proto.InlayHintItem): string | vscode.InlayHintLabelPart[] { + private convertInlayHintText(tsHint: Proto.InlayHintItem): string | vscode.InlayHintLabelPart[] { if (tsHint.displayParts) { return tsHint.displayParts.map((part): vscode.InlayHintLabelPart => { const out = new vscode.InlayHintLabelPart(part.text); if (part.span) { - out.location = Location.fromTextSpan(resource, part.span); + out.location = Location.fromTextSpan(this.client.toResource(part.span.file), part.span); } return out; }); } return tsHint.text; - } } From f605341af6b083f2b6d9c853d882b96955c690b7 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 23 Aug 2023 23:17:23 +0200 Subject: [PATCH 109/607] Syntax highlighting incorrect in vscode.python for the word 'file' (#191111) * Syntax highlighting incorrect in vscode.python for the word 'file' Fixes #188190 * Update colorization test --- extensions/theme-defaults/themes/dark_vs.json | 1 + .../theme-defaults/themes/hc_black.json | 3 +- .../theme-defaults/themes/hc_light.json | 6 +++- .../theme-defaults/themes/light_vs.json | 3 +- .../themes/kimbie-dark-color-theme.json | 3 +- .../themes/dimmed-monokai-color-theme.json | 3 +- .../themes/monokai-color-theme.json | 3 +- .../themes/quietlight-color-theme.json | 3 +- .../theme-red/themes/Red-color-theme.json | 3 +- .../themes/solarized-dark-color-theme.json | 3 +- .../themes/solarized-light-color-theme.json | 3 +- .../tomorrow-night-blue-color-theme.json | 3 +- .../test/colorize-results/test_py.json | 32 +++++++++---------- 13 files changed, 42 insertions(+), 27 deletions(-) diff --git a/extensions/theme-defaults/themes/dark_vs.json b/extensions/theme-defaults/themes/dark_vs.json index 21af2d3cf3a..2b9f0d5a5ab 100644 --- a/extensions/theme-defaults/themes/dark_vs.json +++ b/extensions/theme-defaults/themes/dark_vs.json @@ -34,6 +34,7 @@ "meta.embedded", "source.groovy.embedded", "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#D4D4D4" diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index 26dadd320a0..816fbf9395a 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -19,7 +19,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#FFFFFF" diff --git a/extensions/theme-defaults/themes/hc_light.json b/extensions/theme-defaults/themes/hc_light.json index fde5393f070..17c1af9ef34 100644 --- a/extensions/theme-defaults/themes/hc_light.json +++ b/extensions/theme-defaults/themes/hc_light.json @@ -3,7 +3,11 @@ "name": "Light High Contrast", "tokenColors": [ { - "scope": ["meta.embedded", "source.groovy.embedded"], + "scope": [ + "meta.embedded", + "source.groovy.embedded", + "variable.legacy.builtin.python" + ], "settings": { "foreground": "#292929" } diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index 301730e078e..5e2d5a7889e 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -38,7 +38,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#000000ff" diff --git a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json index eeb4eeb6b88..3554c486209 100644 --- a/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json +++ b/extensions/theme-kimbie-dark/themes/kimbie-dark-color-theme.json @@ -64,7 +64,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#d3af86" diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index ea84bededd5..691680512a4 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -71,7 +71,8 @@ { "scope": [ "meta.embedded", - "source.groovy.embedded" + "source.groovy.embedded", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#C5C8C6" diff --git a/extensions/theme-monokai/themes/monokai-color-theme.json b/extensions/theme-monokai/themes/monokai-color-theme.json index 6489b0dd39c..9a510748714 100644 --- a/extensions/theme-monokai/themes/monokai-color-theme.json +++ b/extensions/theme-monokai/themes/monokai-color-theme.json @@ -111,7 +111,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#F8F8F2" diff --git a/extensions/theme-quietlight/themes/quietlight-color-theme.json b/extensions/theme-quietlight/themes/quietlight-color-theme.json index 9d55f2e362b..3705ed48608 100644 --- a/extensions/theme-quietlight/themes/quietlight-color-theme.json +++ b/extensions/theme-quietlight/themes/quietlight-color-theme.json @@ -10,7 +10,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#333333" diff --git a/extensions/theme-red/themes/Red-color-theme.json b/extensions/theme-red/themes/Red-color-theme.json index c139400dc56..233fd9e83da 100644 --- a/extensions/theme-red/themes/Red-color-theme.json +++ b/extensions/theme-red/themes/Red-color-theme.json @@ -70,7 +70,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#F8F8F8" diff --git a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json index e10c6e67403..e67135a9d99 100644 --- a/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json +++ b/extensions/theme-solarized-dark/themes/solarized-dark-color-theme.json @@ -10,7 +10,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#839496" diff --git a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json index 8b4074c9a07..d5f6dc11bf0 100644 --- a/extensions/theme-solarized-light/themes/solarized-light-color-theme.json +++ b/extensions/theme-solarized-light/themes/solarized-light-color-theme.json @@ -10,7 +10,8 @@ "scope": [ "meta.embedded", "source.groovy.embedded", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { "foreground": "#657B83" diff --git a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json index 8e24e6fe4de..b0bdf8e90a9 100644 --- a/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json +++ b/extensions/theme-tomorrow-night-blue/themes/tomorrow-night-blue-color-theme.json @@ -70,7 +70,8 @@ "meta.embedded", "source.groovy.embedded", "meta.jsx.children", - "string meta.image.inline.markdown" + "string meta.image.inline.markdown", + "variable.legacy.builtin.python" ], "settings": { //"background": "#002451", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_py.json b/extensions/vscode-colorize-tests/test/colorize-results/test_py.json index e858cd5f201..e8d718cad72 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_py.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_py.json @@ -1907,14 +1907,14 @@ "c": "reduce", "t": "source.python meta.function-call.python variable.legacy.builtin.python", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "dark_modern": "variable: #9CDCFE", - "hc_light": "variable: #001080", - "light_modern": "variable: #001080" + "dark_plus": "variable.legacy.builtin.python: #D4D4D4", + "light_plus": "variable.legacy.builtin.python: #000000", + "dark_vs": "variable.legacy.builtin.python: #D4D4D4", + "light_vs": "variable.legacy.builtin.python: #000000", + "hc_black": "variable.legacy.builtin.python: #FFFFFF", + "dark_modern": "variable.legacy.builtin.python: #D4D4D4", + "hc_light": "variable.legacy.builtin.python: #292929", + "light_modern": "variable.legacy.builtin.python: #000000" } }, { @@ -6233,14 +6233,14 @@ "c": "raw_input", "t": "source.python meta.function-call.python variable.legacy.builtin.python", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "dark_modern": "variable: #9CDCFE", - "hc_light": "variable: #001080", - "light_modern": "variable: #001080" + "dark_plus": "variable.legacy.builtin.python: #D4D4D4", + "light_plus": "variable.legacy.builtin.python: #000000", + "dark_vs": "variable.legacy.builtin.python: #D4D4D4", + "light_vs": "variable.legacy.builtin.python: #000000", + "hc_black": "variable.legacy.builtin.python: #FFFFFF", + "dark_modern": "variable.legacy.builtin.python: #D4D4D4", + "hc_light": "variable.legacy.builtin.python: #292929", + "light_modern": "variable.legacy.builtin.python: #000000" } }, { From 9f9ac662035fb28bac3d373d1385f2f0cf24143f Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 23 Aug 2023 14:35:50 -0700 Subject: [PATCH 110/607] notebook-related bugfixes for quick search (#191130) --- .../quickTextSearch/textSearchQuickAccess.ts | 4 ++++ .../contrib/search/browser/searchModel.ts | 21 +++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index d17d209b9b1..d01ad5ad73b 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -230,6 +230,10 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider | Promise | FastAndSlowPicks> | FastAndSlowPicks | null { + if (contentPattern === '') { + this.searchModel.searchResult.clear(); + return []; + } const allMatches = this.doSearch(contentPattern, token); if (!allMatches) { diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 95a4644baf4..0d8c8ec06c0 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -1142,13 +1142,16 @@ export class FolderMatch extends Disposable { raw.forEach(rawFileMatch => { const existingFileMatch = this.getDownstreamFileMatch(rawFileMatch.resource); if (existingFileMatch) { - rawFileMatch - .results! - .filter(resultIsMatch) - .forEach(m => { - textSearchResultToMatches(m, existingFileMatch) - .forEach(m => existingFileMatch.add(m)); - }); + + if (rawFileMatch.results) { + rawFileMatch + .results + .filter(resultIsMatch) + .forEach(m => { + textSearchResultToMatches(m, existingFileMatch) + .forEach(m => existingFileMatch.add(m)); + }); + } // add cell matches if (isIFileMatchWithCells(rawFileMatch)) { @@ -2009,7 +2012,7 @@ export class SearchModel extends Disposable { } { const asyncGenerateOnProgress = async (p: ISearchProgressItem) => { progressEmitter.fire(); - this.onSearchProgress(p, searchInstanceID); + this.onSearchProgress(p, searchInstanceID, false); onProgress?.(p); }; @@ -2020,7 +2023,7 @@ export class SearchModel extends Disposable { }; const tokenSource = this.currentCancelTokenSource = new CancellationTokenSource(callerToken); - const notebookResult = this.notebookSearchService.notebookSearch(query, tokenSource.token, searchInstanceID, syncGenerateOnProgress); + const notebookResult = this.notebookSearchService.notebookSearch(query, tokenSource.token, searchInstanceID, asyncGenerateOnProgress); const textResult = this.searchService.textSearchSplitSyncAsync( searchQuery, this.currentCancelTokenSource.token, asyncGenerateOnProgress, From d91da92c704ab5a054a12566a7c147853e13ed47 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 23 Aug 2023 14:36:39 -0700 Subject: [PATCH 111/607] testing: fix treatment of first child test items (#191131) Fixes #190976 --- .../testing/browser/explorerProjections/treeProjection.ts | 4 ++-- .../workbench/contrib/testing/browser/testingExplorerView.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts b/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts index fe492af83eb..3e67da4150b 100644 --- a/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts +++ b/src/vs/workbench/contrib/testing/browser/explorerProjections/treeProjection.ts @@ -224,7 +224,7 @@ export class TreeProjection extends Disposable implements ITestTreeProjection { } // The first element will cause the root to be hidden - const affectsRootElement = toRemove.parent?.children.size === 1; + const affectsRootElement = toRemove.depth === 1 && toRemove.parent?.children.size === 1; this.changedParents.add(affectsRootElement ? null : toRemove.parent); const queue: Iterable[] = [[toRemove]]; @@ -302,7 +302,7 @@ export class TreeProjection extends Disposable implements ITestTreeProjection { this.items.set(treeElement.test.item.extId, treeElement); // The first element will cause the root to be shown - const affectsRootElement = treeElement.parent?.children.size === 1; + const affectsRootElement = treeElement.depth === 1 && treeElement.parent?.children.size === 1; this.changedParents.add(affectsRootElement ? null : treeElement.parent); if (treeElement.depth === 0 || isCollapsedInSerializedTestTree(this.lastState, treeElement.test.item.extId) === false) { diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 5080d93ece2..84e09da8c8f 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -993,7 +993,7 @@ class TestingExplorerViewModel extends Disposable { this.projection.value = this.instantiationService.createInstance(TreeProjection, lastState); } - const scheduler = new RunOnceScheduler(() => this.applyProjectionChanges(), 200); + const scheduler = this._register(new RunOnceScheduler(() => this.applyProjectionChanges(), 200)); this.projection.value.onUpdate(() => { if (!scheduler.isScheduled()) { scheduler.schedule(); From 7b707177921e2ee40ffcb277544673b16532806e Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 23 Aug 2023 15:13:21 -0700 Subject: [PATCH 112/607] Fix #190631. Emit outline change event. (#191135) --- .../notebook/browser/contrib/outline/notebookOutline.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 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 36a8c18f69b..cf6589b6027 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts @@ -208,6 +208,7 @@ export class NotebookCellOutline implements IOutline { } private _outlineProvider: NotebookCellOutlineProvider | undefined; + private _localDisposables = new DisposableStore(); constructor( private readonly _editor: INotebookEditorPane, @@ -221,9 +222,14 @@ export class NotebookCellOutline implements IOutline { if (!notebookEditor?.hasModel()) { this._outlineProvider?.dispose(); this._outlineProvider = undefined; + this._localDisposables.clear(); } else { this._outlineProvider?.dispose(); + this._localDisposables.clear(); this._outlineProvider = instantiationService.createInstance(NotebookCellOutlineProvider, notebookEditor, _target); + this._localDisposables.add(this._outlineProvider.onDidChange(e => { + this._onDidChange.fire(e); + })); } }; @@ -231,8 +237,6 @@ export class NotebookCellOutline implements IOutline { installSelectionListener(); })); - - installSelectionListener(); const treeDataSource: IDataSource = { getChildren: parent => parent instanceof NotebookCellOutline ? (this._outlineProvider?.entries ?? []) : parent.children }; const delegate = new NotebookOutlineVirtualDelegate(); @@ -315,6 +319,7 @@ export class NotebookCellOutline implements IOutline { this._dispoables.dispose(); this._entriesDisposables.dispose(); this._outlineProvider?.dispose(); + this._localDisposables.dispose(); } } From bf9604c5687aba630ec949738f4e683a042e6b0c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Aug 2023 15:15:07 -0700 Subject: [PATCH 113/607] Add event for when inlay hints are provided (#191134) --- .../src/languageFeatures/inlayHints.ts | 39 ++++++++++++++----- .../src/languageProvider.ts | 2 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts index 1b9d554688d..4fa38e4986b 100644 --- a/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts +++ b/extensions/typescript-language-features/src/languageFeatures/inlayHints.ts @@ -6,6 +6,7 @@ import * as vscode from 'vscode'; import { DocumentSelector } from '../configuration/documentSelector'; import { LanguageDescription } from '../configuration/languageDescription'; +import { TelemetryReporter } from '../logging/telemetry'; import { API } from '../tsServer/api'; import type * as Proto from '../tsServer/protocol/protocol'; import { Location, Position } from '../typeConverters'; @@ -29,13 +30,16 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin public static readonly minVersion = API.v440; - private readonly _onDidChangeInlayHints = new vscode.EventEmitter(); + private readonly _onDidChangeInlayHints = this._register(new vscode.EventEmitter()); public readonly onDidChangeInlayHints = this._onDidChangeInlayHints.event; + private hasReportedTelemetry = false; + constructor( private readonly language: LanguageDescription, private readonly client: ITypeScriptServiceClient, - private readonly fileConfigurationManager: FileConfigurationManager + private readonly fileConfigurationManager: FileConfigurationManager, + private readonly telemetryReporter: TelemetryReporter, ) { super(); @@ -54,31 +58,47 @@ class TypeScriptInlayHintsProvider extends Disposable implements vscode.InlayHin })); } - async provideInlayHints(model: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise { + async provideInlayHints(model: vscode.TextDocument, range: vscode.Range, token: vscode.CancellationToken): Promise { const filepath = this.client.toOpenTsFilePath(model); if (!filepath) { - return []; + return; } if (!areInlayHintsEnabledForFile(this.language, model)) { - return []; + return; } const start = model.offsetAt(range.start); const length = model.offsetAt(range.end) - start; await this.fileConfigurationManager.ensureConfigurationForDocument(model, token); + if (token.isCancellationRequested) { + return; + } + + if (!this.hasReportedTelemetry) { + this.hasReportedTelemetry = true; + /* __GDPR__ + "inlayHints.provide" : { + "owner": "mjbvz", + "${include}": [ + "${TypeScriptCommonProperties}" + ] + } + */ + this.telemetryReporter.logTelemetry('inlayHints.provide', {}); + } const response = await this.client.execute('provideInlayHints', { file: filepath, start, length }, token); if (response.type !== 'response' || !response.success || !response.body) { - return []; + return; } return response.body.map(hint => { const result = new vscode.InlayHint( Position.fromLocation(hint.position), this.convertInlayHintText(hint), - hint.kind && fromProtocolInlayHintKind(hint.kind) + fromProtocolInlayHintKind(hint.kind) ); result.paddingLeft = hint.whitespaceBefore; result.paddingRight = hint.whitespaceAfter; @@ -127,13 +147,14 @@ export function register( selector: DocumentSelector, language: LanguageDescription, client: ITypeScriptServiceClient, - fileConfigurationManager: FileConfigurationManager + fileConfigurationManager: FileConfigurationManager, + telemetryReporter: TelemetryReporter, ) { return conditionalRegistration([ requireMinVersion(client, TypeScriptInlayHintsProvider.minVersion), requireSomeCapability(client, ClientCapability.Semantic), ], () => { - const provider = new TypeScriptInlayHintsProvider(language, client, fileConfigurationManager); + const provider = new TypeScriptInlayHintsProvider(language, client, fileConfigurationManager, telemetryReporter); return vscode.languages.registerInlayHintsProvider(selector.semantic, provider); }); } diff --git a/extensions/typescript-language-features/src/languageProvider.ts b/extensions/typescript-language-features/src/languageProvider.ts index 1de34c6998c..7acbf733f0c 100644 --- a/extensions/typescript-language-features/src/languageProvider.ts +++ b/extensions/typescript-language-features/src/languageProvider.ts @@ -74,7 +74,7 @@ export default class LanguageProvider extends Disposable { import('./languageFeatures/formatting').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), import('./languageFeatures/hover').then(provider => this._register(provider.register(selector, this.client, this.fileConfigurationManager))), import('./languageFeatures/implementations').then(provider => this._register(provider.register(selector, this.client))), - import('./languageFeatures/inlayHints').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), + import('./languageFeatures/inlayHints').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager, this.telemetryReporter))), import('./languageFeatures/jsDocCompletions').then(provider => this._register(provider.register(selector, this.description, this.client, this.fileConfigurationManager))), import('./languageFeatures/linkedEditing').then(provider => this._register(provider.register(selector, this.client))), import('./languageFeatures/organizeImports').then(provider => this._register(provider.register(selector, this.client, this.commandManager, this.fileConfigurationManager, this.telemetryReporter))), From 60c8cb0be41b6f666418564013d1e39ac26d514d Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 23 Aug 2023 15:19:38 -0700 Subject: [PATCH 114/607] Re #183449. Roaming kernel history. --- .../notebookKernelHistoryServiceImpl.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts index 7256ad1d404..0a19cda94bf 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LinkedMap, Touch } from 'vs/base/common/map'; import { localize } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; @@ -36,6 +36,9 @@ export class NotebookKernelHistoryService extends Disposable implements INoteboo this._loadState(); this._register(this._storageService.onWillSaveState(() => this._saveState())); + this._register(this._storageService.onDidChangeValue(StorageScope.WORKSPACE, NotebookKernelHistoryService.STORAGE_KEY, this._register(new DisposableStore()))(() => { + this._restoreState(); + })); } getKernels(notebook: INotebookTextModelLike): { selected: INotebookKernel | undefined; all: INotebookKernel[] } { @@ -79,12 +82,28 @@ export class NotebookKernelHistoryService extends Disposable implements INoteboo if (notEmpty) { const serialized = this._serialize(); - this._storageService.store(NotebookKernelHistoryService.STORAGE_KEY, JSON.stringify(serialized), StorageScope.WORKSPACE, StorageTarget.MACHINE); + this._storageService.store(NotebookKernelHistoryService.STORAGE_KEY, JSON.stringify(serialized), StorageScope.WORKSPACE, StorageTarget.USER); } else { this._storageService.remove(NotebookKernelHistoryService.STORAGE_KEY, StorageScope.WORKSPACE); } } + private _restoreState(): void { + const serialized = this._storageService.get(NotebookKernelHistoryService.STORAGE_KEY, StorageScope.WORKSPACE); + if (serialized) { + try { + for (const [viewType, kernels] of JSON.parse(serialized)) { + const linkedMap = this._mostRecentKernelsMap[viewType] ?? new LinkedMap(); + for (const entry of kernels.entries) { + linkedMap.set(entry, entry, Touch.AsOld); + } + } + } catch (e) { + console.error('Deserialize notebook kernel history failed', e); + } + } + } + private _loadState(): void { const serialized = this._storageService.get(NotebookKernelHistoryService.STORAGE_KEY, StorageScope.WORKSPACE); if (serialized) { From e4a2928d14b38a67e334a120d447ded29e0f562c Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 23 Aug 2023 15:47:35 -0700 Subject: [PATCH 115/607] bump distro (#191139) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 584247e9fa4..2e5dcf01a2e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "1591281180fd2cd18935e6847131d2d4213b7b69", + "distro": "56bfb9ce4a8d2298f475a1b8d9c7a7b5a72204f2", "author": { "name": "Microsoft Corporation" }, From b82a7222adc865e8a27b1b14c1fd11ab2900f23e Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 23 Aug 2023 15:49:45 -0700 Subject: [PATCH 116/607] Fix chat response file tree border radius (#191142) --- .../workbench/contrib/chat/browser/chatListRenderer.css | 8 -------- src/vs/workbench/contrib/chat/browser/media/chat.css | 8 ++++++-- 2 files changed, 6 insertions(+), 10 deletions(-) delete mode 100644 src/vs/workbench/contrib/chat/browser/chatListRenderer.css diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.css b/src/vs/workbench/contrib/chat/browser/chatListRenderer.css deleted file mode 100644 index 49c3ab7833c..00000000000 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.css +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.interactive-response-progress-tree .monaco-tl-row:hover { - background-color: var(--vscode-list-hoverBackground); -} diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index ab5e3bd6c59..33b798459ea 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -436,8 +436,6 @@ .interactive-response-progress-tree { margin: 16px 0px; - border-radius: 4px; - border: 1px solid var(--vscode-input-border, transparent); } .interactive-response-progress-tree.focused { @@ -458,3 +456,9 @@ align-items: start; gap: 6px; } + +.interactive-response-progress-tree .monaco-list .monaco-scrollable-element .monaco-list-rows { + border: 1px solid var(--vscode-input-border,transparent); + border-radius: 4px; + width: auto; +} From 9a69c2ab7ca5e81f513a00179b717a8b9bc2217a Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 23 Aug 2023 16:03:45 -0700 Subject: [PATCH 117/607] One Provider per Type (#191136) Simpler design due to feedback from Logan. --- .../browser/mainThreadAiRelatedInformation.ts | 11 ++++--- .../workbench/api/common/extHost.api.impl.ts | 4 +-- .../workbench/api/common/extHost.protocol.ts | 4 +-- .../api/common/extHostAiRelatedInformation.ts | 11 ++++--- .../common/aiRelatedInformation.ts | 12 ++++---- .../common/aiRelatedInformationService.ts | 29 +++++++++---------- .../vscode.proposed.aiRelatedInformation.d.ts | 27 +++++------------ 7 files changed, 41 insertions(+), 57 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts b/src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts index b198f41be47..e254b5f4149 100644 --- a/src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts +++ b/src/vs/workbench/api/browser/mainThreadAiRelatedInformation.ts @@ -7,9 +7,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; import { ExtHostAiRelatedInformationShape, ExtHostContext, MainContext, MainThreadAiRelatedInformationShape } from 'vs/workbench/api/common/extHost.protocol'; import { RelatedInformationType } from 'vs/workbench/api/common/extHostTypes'; -import { IAiRelatedInformationProvider, IAiRelatedInformationService } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; +import { IAiRelatedInformationProvider, IAiRelatedInformationService, RelatedInformationResult } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { RelatedInformationResult } from 'vscode'; @extHostNamedCustomer(MainContext.MainThreadAiRelatedInformation) export class MainThreadAiRelatedInformation extends Disposable implements MainThreadAiRelatedInformationShape { @@ -29,13 +28,13 @@ export class MainThreadAiRelatedInformation extends Disposable implements MainTh return this._aiRelatedInformationService.getRelatedInformation(query, types, CancellationToken.None); } - $registerAiRelatedInformationProvider(handle: number, types: RelatedInformationType[]): void { + $registerAiRelatedInformationProvider(handle: number, type: RelatedInformationType): void { const provider: IAiRelatedInformationProvider = { - provideAiRelatedInformation: (query, types, token) => { - return this._proxy.$provideAiRelatedInformation(handle, query, types, token); + provideAiRelatedInformation: (query, token) => { + return this._proxy.$provideAiRelatedInformation(handle, query, token); }, }; - this._registrations.set(handle, this._aiRelatedInformationService.registerAiRelatedInformationProvider(types, provider)); + this._registrations.set(handle, this._aiRelatedInformationService.registerAiRelatedInformationProvider(type, provider)); } $unregisterAiRelatedInformationProvider(handle: number): void { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f9803e1476f..acdb35b1b1e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1332,9 +1332,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'aiRelatedInformation'); return extHostAiRelatedInformation.getRelatedInformation(extension, query, types); }, - registerRelatedInformationProvider(types: vscode.RelatedInformationType[], provider: vscode.RelatedInformationProvider) { + registerRelatedInformationProvider(type: vscode.RelatedInformationType, provider: vscode.RelatedInformationProvider) { checkProposedApiEnabled(extension, 'aiRelatedInformation'); - return extHostAiRelatedInformation.registerRelatedInformationProvider(extension, types, provider); + return extHostAiRelatedInformation.registerRelatedInformationProvider(extension, type, provider); }, registerEmbeddingVectorProvider(model: string, provider: vscode.EmbeddingVectorProvider) { checkProposedApiEnabled(extension, 'aiRelatedInformation'); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 93826ca0f5f..37e0b6c9ba9 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1675,12 +1675,12 @@ export interface MainThreadSemanticSimilarityShape extends IDisposable { } export interface ExtHostAiRelatedInformationShape { - $provideAiRelatedInformation(handle: number, query: string, types: RelatedInformationType[], token: CancellationToken): Promise; + $provideAiRelatedInformation(handle: number, query: string, token: CancellationToken): Promise; } export interface MainThreadAiRelatedInformationShape { $getAiRelatedInformation(query: string, types: RelatedInformationType[]): Promise; - $registerAiRelatedInformationProvider(handle: number, types: RelatedInformationType[]): void; + $registerAiRelatedInformationProvider(handle: number, type: RelatedInformationType): void; $unregisterAiRelatedInformationProvider(handle: number): void; } diff --git a/src/vs/workbench/api/common/extHostAiRelatedInformation.ts b/src/vs/workbench/api/common/extHostAiRelatedInformation.ts index 9dc39e42ae1..4cb934e1b1e 100644 --- a/src/vs/workbench/api/common/extHostAiRelatedInformation.ts +++ b/src/vs/workbench/api/common/extHostAiRelatedInformation.ts @@ -5,7 +5,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtHostAiRelatedInformationShape, IMainContext, MainContext, MainThreadAiRelatedInformationShape } from 'vs/workbench/api/common/extHost.protocol'; -import type { CancellationToken, RelatedInformationProvider, RelatedInformationResult, RelatedInformationType } from 'vscode'; +import type { CancellationToken, RelatedInformationProvider, RelatedInformationType, RelatedInformationResult } from 'vscode'; import { Disposable } from 'vs/workbench/api/common/extHostTypes'; export class ExtHostRelatedInformation implements ExtHostAiRelatedInformationShape { @@ -18,7 +18,7 @@ export class ExtHostRelatedInformation implements ExtHostAiRelatedInformationSha this._proxy = mainContext.getProxy(MainContext.MainThreadAiRelatedInformation); } - async $provideAiRelatedInformation(handle: number, query: string, types: RelatedInformationType[], token: CancellationToken): Promise { + async $provideAiRelatedInformation(handle: number, query: string, token: CancellationToken): Promise { if (this._relatedInformationProviders.size === 0) { throw new Error('No semantic similarity providers registered'); } @@ -28,8 +28,7 @@ export class ExtHostRelatedInformation implements ExtHostAiRelatedInformationSha throw new Error('Semantic similarity provider not found'); } - // TODO: should this return undefined or an empty array? - const result = await provider.provideRelatedInformation(query, types, token) ?? []; + const result = await provider.provideRelatedInformation(query, token) ?? []; return result; } @@ -37,11 +36,11 @@ export class ExtHostRelatedInformation implements ExtHostAiRelatedInformationSha return this._proxy.$getAiRelatedInformation(query, types); } - registerRelatedInformationProvider(extension: IExtensionDescription, types: RelatedInformationType[], provider: RelatedInformationProvider): Disposable { + registerRelatedInformationProvider(extension: IExtensionDescription, type: RelatedInformationType, provider: RelatedInformationProvider): Disposable { const handle = this._nextHandle; this._nextHandle++; this._relatedInformationProviders.set(handle, provider); - this._proxy.$registerAiRelatedInformationProvider(handle, types); + this._proxy.$registerAiRelatedInformationProvider(handle, type); return new Disposable(() => { this._proxy.$unregisterAiRelatedInformationProvider(handle); this._relatedInformationProviders.delete(handle); diff --git a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts index a8bddf2c1e4..f3b7d9a090f 100644 --- a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts +++ b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation.ts @@ -16,29 +16,31 @@ export enum RelatedInformationType { SettingInformation = 4 } -export interface RelatedInformationResult { +interface RelatedInformationBaseResult { type: RelatedInformationType; weight: number; } -export interface CommandInformationResult extends RelatedInformationResult { +export interface CommandInformationResult extends RelatedInformationBaseResult { type: RelatedInformationType.CommandInformation; command: string; } -export interface SettingInformationResult extends RelatedInformationResult { +export interface SettingInformationResult extends RelatedInformationBaseResult { type: RelatedInformationType.SettingInformation; setting: string; } +export type RelatedInformationResult = CommandInformationResult | SettingInformationResult; + export interface IAiRelatedInformationService { readonly _serviceBrand: undefined; isEnabled(): boolean; getRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): Promise; - registerAiRelatedInformationProvider(types: RelatedInformationType[], provider: IAiRelatedInformationProvider): IDisposable; + registerAiRelatedInformationProvider(type: RelatedInformationType, provider: IAiRelatedInformationProvider): IDisposable; } export interface IAiRelatedInformationProvider { - provideAiRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): Promise; + provideAiRelatedInformation(query: string, token: CancellationToken): Promise; } diff --git a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts index e0309ff37d1..7168c3b4eed 100644 --- a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts +++ b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts @@ -24,24 +24,21 @@ export class AiRelatedInformationService implements IAiRelatedInformationService return this._providers.size > 0; } - registerAiRelatedInformationProvider(types: RelatedInformationType[], provider: IAiRelatedInformationProvider): IDisposable { - for (const type of types) { - const providers = this._providers.get(type) ?? []; - providers.push(provider); - this._providers.set(type, providers); - } + registerAiRelatedInformationProvider(type: RelatedInformationType, provider: IAiRelatedInformationProvider): IDisposable { + const providers = this._providers.get(type) ?? []; + providers.push(provider); + this._providers.set(type, providers); + return { dispose: () => { - for (const type of types) { - const providers = this._providers.get(type) ?? []; - const index = providers.indexOf(provider); - if (index !== -1) { - providers.splice(index, 1); - } - if (providers.length === 0) { - this._providers.delete(type); - } + const providers = this._providers.get(type) ?? []; + const index = providers.indexOf(provider); + if (index !== -1) { + providers.splice(index, 1); + } + if (providers.length === 0) { + this._providers.delete(type); } } }; @@ -78,7 +75,7 @@ export class AiRelatedInformationService implements IAiRelatedInformationService for (const provider of providers) { cancellablePromises.push(createCancelablePromise(async t => { try { - const result = await provider.provideAiRelatedInformation(query, types, t); + const result = await provider.provideAiRelatedInformation(query, t); // double filter just in case return result.filter(r => types.includes(r.type)); } catch (e) { diff --git a/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts b/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts index d3916c50608..e5e28653cec 100644 --- a/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts +++ b/src/vscode-dts/vscode.proposed.aiRelatedInformation.d.ts @@ -7,13 +7,6 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/190909 - export interface SearchResult { - // from Andrea - preview: string; - resource: Uri; - location: Range; - } - export enum RelatedInformationType { SymbolInformation = 1, CommandInformation = 2, @@ -21,33 +14,27 @@ declare module 'vscode' { SettingInformation = 4 } - export interface RelatedInformationResult { + interface RelatedInformationBaseResult { type: RelatedInformationType; weight: number; } - export interface SymbolInformationResult extends RelatedInformationResult { - type: RelatedInformationType.SymbolInformation; - symbolInformation: SymbolInformation; - } + // TODO: Symbols and Search - export interface CommandInformationResult extends RelatedInformationResult { + export interface CommandInformationResult extends RelatedInformationBaseResult { type: RelatedInformationType.CommandInformation; command: string; } - export interface SettingInformationResult extends RelatedInformationResult { + export interface SettingInformationResult extends RelatedInformationBaseResult { type: RelatedInformationType.SettingInformation; setting: string; } - export interface SearchInformationResult extends RelatedInformationResult { - type: RelatedInformationType.SearchInformation; - searchResult: SearchResult; - } + export type RelatedInformationResult = CommandInformationResult | SettingInformationResult; export interface RelatedInformationProvider { - provideRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): ProviderResult; + provideRelatedInformation(query: string, token: CancellationToken): ProviderResult; } export interface EmbeddingVectorProvider { @@ -56,7 +43,7 @@ declare module 'vscode' { export namespace ai { export function getRelatedInformation(query: string, types: RelatedInformationType[], token: CancellationToken): Thenable; - export function registerRelatedInformationProvider(types: RelatedInformationType[], provider: RelatedInformationProvider): Disposable; + export function registerRelatedInformationProvider(type: RelatedInformationType, provider: RelatedInformationProvider): Disposable; export function registerEmbeddingVectorProvider(model: string, provider: EmbeddingVectorProvider): Disposable; } } From ba35c622b06307173c04184e0e270673d750a4d2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 23 Aug 2023 16:26:51 -0700 Subject: [PATCH 118/607] Allow InteractiveProgressContent to return a markdown string (#191145) This allows enabling of specific command uris in responses --- src/vs/workbench/api/browser/mainThreadChat.ts | 7 ++++--- .../workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostChat.ts | 4 ++++ .../contrib/chat/browser/chatListRenderer.ts | 9 +++++---- .../workbench/contrib/chat/common/chatModel.ts | 18 +++++++++++------- .../contrib/chat/common/chatService.ts | 2 +- .../contrib/chat/common/chatServiceImpl.ts | 2 +- .../vscode.proposed.interactive.d.ts | 2 +- 8 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChat.ts b/src/vs/workbench/api/browser/mainThreadChat.ts index ddeba6f997b..6b45f162796 100644 --- a/src/vs/workbench/api/browser/mainThreadChat.ts +++ b/src/vs/workbench/api/browser/mainThreadChat.ts @@ -5,6 +5,7 @@ import { DeferredPromise } from 'vs/base/common/async'; import { Emitter } from 'vs/base/common/event'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; import { revive } from 'vs/base/common/marshalling'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -19,13 +20,13 @@ import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/ext export class MainThreadChat extends Disposable implements MainThreadChatShape { private readonly _providerRegistrations = this._register(new DisposableMap()); - private readonly _activeRequestProgressCallbacks = new Map (DeferredPromise | void)>(); + private readonly _activeRequestProgressCallbacks = new Map (DeferredPromise | void)>(); private readonly _stateEmitters = new Map>(); private readonly _proxy: ExtHostChatShape; private _responsePartHandlePool = 0; - private readonly _activeResponsePartPromises = new Map>(); + private readonly _activeResponsePartPromises = new Map>(); constructor( extHostContext: IExtHostContext, @@ -134,7 +135,7 @@ export class MainThreadChat extends Disposable implements MainThreadChatShape { if ('placeholder' in progress) { const responsePartId = `${id}_${++this._responsePartHandlePool}`; - const deferredContentPromise = new DeferredPromise(); + const deferredContentPromise = new DeferredPromise(); this._activeResponsePartPromises.set(responsePartId, deferredContentPromise); this._activeRequestProgressCallbacks.get(id)?.({ ...progress, resolvedContent: deferredContentPromise.p }); return this._responsePartHandlePool; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 37e0b6c9ba9..e4964fb5670 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1216,7 +1216,7 @@ export interface IChatResponseProgressFileTreeData { children?: IChatResponseProgressFileTreeData[]; } -export type IChatResponseProgressDto = { content: string } | { requestId: string } | { placeholder: string } | { treeData: IChatResponseProgressFileTreeData }; +export type IChatResponseProgressDto = { content: string | IMarkdownString } | { requestId: string } | { placeholder: string } | { treeData: IChatResponseProgressFileTreeData }; export interface MainThreadChatShape extends IDisposable { $registerChatProvider(handle: number, id: string): Promise; diff --git a/src/vs/workbench/api/common/extHostChat.ts b/src/vs/workbench/api/common/extHostChat.ts index db4f90727ba..9aa2c625cc9 100644 --- a/src/vs/workbench/api/common/extHostChat.ts +++ b/src/vs/workbench/api/common/extHostChat.ts @@ -232,6 +232,10 @@ export class ExtHostChat implements ExtHostChatShape { const [progressHandle, progressContent] = res; this._proxy.$acceptResponseProgress(handle, sessionId, progressContent, progressHandle ?? undefined); }); + } else if ('content' in progress) { + this._proxy.$acceptResponseProgress(handle, sessionId, { + content: typeof progress.content === 'string' ? progress.content : typeConvert.MarkdownString.from(progress.content) + }); } else { this._proxy.$acceptResponseProgress(handle, sessionId, progress); } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index dad24e99b60..e6ec6bd3ae8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -316,12 +316,12 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer, element: ChatTreeItem, index: number, templateData: IChatListItemTemplate) { const fillInIncompleteTokens = isResponseVM(element) && (!element.isComplete || element.isCanceled || element.errorDetails?.responseIsFiltered || element.errorDetails?.responseIsIncomplete); dom.clearNode(templateData.value); let fileTreeIndex = 0; - for (const data of markdownValue) { + for (const data of value) { const result = 'value' in data ? this.renderMarkdown(data, element, templateData.elementDisposables, templateData, fillInIncompleteTokens) : this.renderTreeData(data, element, templateData.elementDisposables, templateData, fileTreeIndex++); @@ -577,8 +577,9 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer; - updateContent(responsePart: string | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void; + updateContent(responsePart: string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void; asString(): string; } @@ -125,17 +125,21 @@ export class Response implements IResponse { return this._responseRepr; } - updateContent(responsePart: string | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void { - if (typeof responsePart === 'string') { + updateContent(responsePart: string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean): void { + if (typeof responsePart === 'string' || isMarkdownString(responsePart)) { const responsePartLength = this._responseParts.length - 1; const lastResponsePart = this._responseParts[responsePartLength]; if (lastResponsePart.isPlaceholder === true || isCompleteInteractiveProgressTreeData(lastResponsePart)) { // The last part is resolving or a tree data item, start a new part - this._responseParts.push({ string: new MarkdownString(responsePart) }); + this._responseParts.push({ string: typeof responsePart === 'string' ? new MarkdownString(responsePart) : responsePart }); } else { // Combine this part with the last, non-resolving string part - this._responseParts[responsePartLength] = { string: new MarkdownString(lastResponsePart.string.value + responsePart) }; + if (isMarkdownString(responsePart)) { + this._responseParts[responsePartLength] = { string: new MarkdownString(lastResponsePart.string.value + responsePart.value, responsePart) }; + } else { + this._responseParts[responsePartLength] = { string: new MarkdownString(lastResponsePart.string.value + responsePart, lastResponsePart.string) }; + } } this._updateRepr(quiet); @@ -250,7 +254,7 @@ export class ChatResponseModel extends Disposable implements IChatResponseModel this._id = 'response_' + ChatResponseModel.nextId++; } - updateContent(responsePart: string | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean) { + updateContent(responsePart: string | IMarkdownString | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent?: Promise }, quiet?: boolean) { this._response.updateContent(responsePart, quiet); } diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 2812120dccd..9039dfb5150 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -52,7 +52,7 @@ export interface IChatResponseProgressFileTreeData { } export type IChatProgress = - { content: string } | { requestId: string } | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent: Promise }; + { content: string | IMarkdownString } | { requestId: string } | { treeData: IChatResponseProgressFileTreeData } | { placeholder: string; resolvedContent: Promise }; export interface IPersistedChatState { } export interface IChatProvider { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index ae621f87f69..bd759fd4c94 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -450,7 +450,7 @@ export class ChatService extends Disposable implements IChatService { gotProgress = true; if ('content' in progress) { - this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${progress.content.length} chars`); + this.trace('sendRequest', `Provider returned progress for session ${model.sessionId}, ${typeof progress.content === 'string' ? progress.content.length : progress.content.value.length} chars`); } else if ('placeholder' in progress) { this.trace('sendRequest', `Provider returned placeholder for session ${model.sessionId}, ${progress.placeholder}`); } else if (isCompleteInteractiveProgressTreeData(progress)) { diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 382afeb8277..8ec16060be1 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -121,7 +121,7 @@ declare module 'vscode' { } export interface InteractiveProgressContent { - content: string; + content: string | MarkdownString; } export interface InteractiveProgressId { From f7a7d9488fe56fba0851a9f53ab203a5136a799c Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 23 Aug 2023 16:47:31 -0700 Subject: [PATCH 119/607] cli: serve-web listener improvements (#191146) - Allow listening on a socket path (required manually implementing the Accept trait), fixes #191043 - Parse the host syntax correctly, fixes #191067 --- cli/src/async_pipe.rs | 55 ++++++++++++++++++++++++++++++- cli/src/commands/args.rs | 3 ++ cli/src/commands/serve_web.rs | 62 +++++++++++++++++++++-------------- 3 files changed, 94 insertions(+), 26 deletions(-) diff --git a/cli/src/async_pipe.rs b/cli/src/async_pipe.rs index 6c7c918967a..e9b710c1d68 100644 --- a/cli/src/async_pipe.rs +++ b/cli/src/async_pipe.rs @@ -6,6 +6,8 @@ use crate::{constants::APPLICATION_NAME, util::errors::CodeError}; use async_trait::async_trait; use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::task::{Context, Poll}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpListener; use uuid::Uuid; @@ -44,7 +46,7 @@ cfg_if::cfg_if! { } else { use tokio::{time::sleep, io::ReadBuf}; use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions, NamedPipeClient, NamedPipeServer}; - use std::{time::Duration, pin::Pin, task::{Context, Poll}, io}; + use std::{time::Duration, io}; use pin_project::pin_project; #[pin_project(project = AsyncPipeProj)] @@ -174,6 +176,57 @@ cfg_if::cfg_if! { } } +impl AsyncPipeListener { + pub fn into_pollable(self) -> PollableAsyncListener { + PollableAsyncListener { + listener: Some(self), + write_fut: tokio_util::sync::ReusableBoxFuture::new(make_accept_fut(None)), + } + } +} + +pub struct PollableAsyncListener { + listener: Option, + write_fut: tokio_util::sync::ReusableBoxFuture< + 'static, + (AsyncPipeListener, Result), + >, +} + +async fn make_accept_fut( + data: Option, +) -> (AsyncPipeListener, Result) { + match data { + Some(mut l) => { + let c = l.accept().await; + (l, c) + } + None => unreachable!("this future should not be pollable in this state"), + } +} + +impl hyper::server::accept::Accept for PollableAsyncListener { + type Conn = AsyncPipe; + type Error = CodeError; + + fn poll_accept( + mut self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll>> { + if let Some(l) = self.listener.take() { + self.write_fut.set(make_accept_fut(Some(l))) + } + + match self.write_fut.poll(cx) { + Poll::Ready((l, cnx)) => { + self.listener = Some(l); + Poll::Ready(Some(cnx)) + } + Poll::Pending => Poll::Pending, + } + } +} + /// Gets a random name for a pipe/socket on the paltform pub fn get_socket_name() -> PathBuf { cfg_if::cfg_if! { diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index cce01c52fd9..bfa1c6f2da4 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -185,6 +185,9 @@ pub struct ServeWebArgs { /// Host to listen on, defaults to 'localhost' #[clap(long)] pub host: Option, + // The path to a socket file for the server to listen to. + #[clap(long)] + pub socket_path: Option, /// Port to listen on. If 0 is passed a random free port is picked. #[clap(long, default_value_t = 8000)] pub port: u16, diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs index b2bf4d431e4..4a3af432444 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -16,7 +16,9 @@ use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::pin; use tokio::process::Command; -use crate::async_pipe::{get_socket_name, get_socket_rw_stream, AsyncPipe}; +use crate::async_pipe::{ + get_socket_name, get_socket_rw_stream, listen_socket_rw_stream, AsyncPipe, +}; use crate::constants::VSCODE_CLI_QUALITY; use crate::download_cache::DownloadCache; use crate::log; @@ -53,43 +55,53 @@ const RELEASE_CACHE_SECS: u64 = 60 * 60; /// while new clients get new VS Code Server versions. pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result { legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; - let mut addr: SocketAddr = match &args.host { - Some(h) => h.parse().map_err(CodeError::InvalidHostAddress)?, - None => SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0), - }; - addr.set_port(args.port); let platform: crate::update_service::Platform = PreReqChecker::new().verify().await?; if !args.without_connection_token { // Ensure there's a defined connection token, since if multiple server versions // are excuted, they will need to have a single shared token. - let connection_token = args - .connection_token - .clone() - .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); - ctx.log.result(format!( - "Web UI available at http://{}?tkn={}", - addr, connection_token, - )); - args.connection_token = Some(connection_token); - } else { - ctx.log - .result(format!("Web UI available at http://{}", addr)); - args.connection_token = None; + args.connection_token = Some( + args.connection_token + .clone() + .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()), + ); } - let cm = ConnectionManager::new(&ctx, platform, args); - let make_svc = make_service_fn(move |_conn| { + let cm = ConnectionManager::new(&ctx, platform, args.clone()); + let make_svc = move || { let cm = cm.clone(); - let log = ctx.log.clone(); + let log = cm.log.clone(); let service = service_fn(move |req| handle(cm.clone(), log.clone(), req)); async move { Ok::<_, Infallible>(service) } - }); + }; - let server = Server::bind(&addr).serve(make_svc); + let r = if let Some(s) = args.socket_path { + let socket = listen_socket_rw_stream(&PathBuf::from(&s)).await?; + ctx.log.result(format!("Web UI available on {}", s)); + Server::builder(socket.into_pollable()) + .serve(make_service_fn(|_| make_svc())) + .await + } else { + let addr: SocketAddr = match &args.host { + Some(h) => { + SocketAddr::new(h.parse().map_err(CodeError::InvalidHostAddress)?, args.port) + } + None => SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), args.port), + }; - server.await.map_err(CodeError::CouldNotListenOnInterface)?; + let mut listening = format!("Web UI available at http://{}", addr); + if let Some(ct) = args.connection_token { + listening.push_str(&format!("?tkn={}", ct)); + } + ctx.log.result(listening); + + Server::bind(&addr) + .serve(make_service_fn(|_| make_svc())) + .await + }; + + r.map_err(CodeError::CouldNotListenOnInterface)?; Ok(0) } From e23be75182cabbf88bb2662d55c5c34c1f9a50f4 Mon Sep 17 00:00:00 2001 From: Ole Date: Thu, 24 Aug 2023 01:55:41 +0200 Subject: [PATCH 120/607] Increase shortcut consistency of web with electron. (#191061) On electron, nothing changes. On web, * "Focus Application Menu" changes from F10 to Alt+F10 (this action only exists on web) * "Debugger: Step over" changes from Alt+F10 to F10 like on electron While #183510 already added the F10 binding for the debugger also on web, it did not actually have any effect, because of a conflict with F10 for "Focus Application Menu" on web. This change was agreed upon in #190180 due to the relatively low usage of "Open Application Menu" in the interest of more overall consistency. Fixes #190180. --- src/vs/workbench/browser/parts/titlebar/menubarControl.ts | 4 ++-- src/vs/workbench/contrib/debug/browser/debugCommands.ts | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index e96e773a916..ec47bd48bca 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -32,7 +32,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { isFullscreen } from 'vs/base/browser/browser'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; -import { KeyCode } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IsMacNativeContext, IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -440,7 +440,7 @@ export class CustomMenubarControl extends MenubarControl { id: `workbench.actions.menubar.focus`, title: { value: localize('focusMenu', "Focus Application Menu"), original: 'Focus Application Menu' }, keybinding: { - primary: KeyCode.F10, + primary: KeyMod.Alt | KeyCode.F10, weight: KeybindingWeight.WorkbenchContrib, when: IsWebContext }, diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 9515ab1ad73..1fecc75d4f9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -474,7 +474,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: STEP_OVER_ID, weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.F10, - secondary: isWeb ? [(KeyMod.Alt | KeyCode.F10)] : undefined, // Keep Alt-F10 for web for backwards-compatibility when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { const contextKeyService = accessor.get(IContextKeyService); From a0377f0c51dbb2d3188565cdf35e89929f864e65 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Wed, 23 Aug 2023 20:42:18 -0700 Subject: [PATCH 121/607] Fix #189809. Update tree options paddingBottom. (#191143) --- src/vs/base/browser/ui/tree/abstractTree.ts | 2 +- src/vs/platform/list/browser/listService.ts | 4 ++-- src/vs/workbench/contrib/files/browser/views/explorerView.ts | 2 +- src/vs/workbench/contrib/search/browser/searchView.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 3591193bab9..70199a8164c 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1218,7 +1218,7 @@ export interface IAbstractTreeOptions extends IAbstractTr readonly collapseByDefault?: boolean; // defaults to false readonly filter?: ITreeFilter; readonly dnd?: ITreeDragAndDrop; - readonly additionalScrollHeight?: number; + readonly paddingBottom?: number; readonly findWidgetEnabled?: boolean; readonly findWidgetStyles?: IFindWidgetStyles; readonly defaultFindVisibility?: TreeVisibility | ((e: T) => TreeVisibility); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 7c5a1eb9cd6..6b698bc17f6 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1145,7 +1145,7 @@ function workbenchTreeDataPreamble(treeRenderIndentGuidesKey); return { @@ -1162,7 +1162,7 @@ function workbenchTreeDataPreamble(treeExpandMode) === 'doubleClick'), contextViewProvider: contextViewService as IContextViewProvider, diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 76b81a7ed42..55039bcddd0 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -450,7 +450,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { } return false; }, - additionalScrollHeight: ExplorerDelegate.ITEM_HEIGHT, + paddingBottom: ExplorerDelegate.ITEM_HEIGHT, overrideStyles: { listBackground: SIDE_BAR_BACKGROUND } diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 4cdc0341049..b87d1347816 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -860,7 +860,7 @@ export class SearchView extends ViewPane { overrideStyles: { listBackground: this.getBackgroundColor() }, - additionalScrollHeight: SearchDelegate.ITEM_HEIGHT + paddingBottom: SearchDelegate.ITEM_HEIGHT })); this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); const updateHasSomeCollapsible = () => this.toggleCollapseStateDelayer.trigger(() => this.hasSomeCollapsibleResultKey.set(this.hasSomeCollapsible())); From d6330cc2a58af3e2658f6acdd88ab1263e4d0d1c Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 23 Aug 2023 20:43:36 -0700 Subject: [PATCH 122/607] Update cached map --- .../browser/services/notebookKernelHistoryServiceImpl.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts index 0a19cda94bf..7901ccc3e16 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts @@ -97,6 +97,8 @@ export class NotebookKernelHistoryService extends Disposable implements INoteboo for (const entry of kernels.entries) { linkedMap.set(entry, entry, Touch.AsOld); } + + this._mostRecentKernelsMap[viewType] = linkedMap; } } catch (e) { console.error('Deserialize notebook kernel history failed', e); From 94956b4c3fb38badce912cae3d5a531502669885 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 23 Aug 2023 20:55:52 -0700 Subject: [PATCH 123/607] Fix #189673. Tracking original output id that is reused. --- .../contrib/notebook/browser/notebook.contribution.ts | 4 ++-- .../common/model/notebookCellOutputTextModel.ts | 11 +++++++++++ .../contrib/notebook/common/notebookCommon.ts | 4 ++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 8bbe932cb1f..2ef3dfb4e8e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -455,7 +455,7 @@ class CellInfoContentProvider { let result: { content: string; mode: ILanguageSelection } | undefined = undefined; const mode = this._languageService.createById('json'); - const op = cell.outputs.find(op => op.outputId === data.outputId); + const op = cell.outputs.find(op => op.outputId === data.outputId || op.alternativeOutputId === data.outputId); const streamOutputData = this.parseStreamOutput(op); if (streamOutputData) { result = streamOutputData; @@ -491,7 +491,7 @@ class CellInfoContentProvider { } const ref = await this._notebookModelResolverService.resolve(data.notebook); - const cell = ref.object.notebook.cells.find(cell => !!cell.outputs.find(op => op.outputId === data.outputId)); + const cell = ref.object.notebook.cells.find(cell => !!cell.outputs.find(op => op.outputId === data.outputId || op.alternativeOutputId === data.outputId)); if (!cell) { ref.dispose(); diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts index 85ed20571d1..2e2ad62c97e 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellOutputTextModel.ts @@ -25,6 +25,15 @@ export class NotebookCellOutputTextModel extends Disposable implements ICellOutp return this._rawOutput.outputId; } + /** + * Alternative output id that's reused when the output is updated. + */ + private _alternativeOutputId: string; + + get alternativeOutputId(): string { + return this._alternativeOutputId; + } + private _versionId = 0; get versionId() { @@ -35,6 +44,8 @@ export class NotebookCellOutputTextModel extends Disposable implements ICellOutp private _rawOutput: IOutputDto ) { super(); + + this._alternativeOutputId = this._rawOutput.outputId; } replaceData(rawData: IOutputDto) { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index e01a5c37361..8d06fb4cec3 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -214,6 +214,10 @@ export interface ICellOutput { outputs: IOutputItemDto[]; metadata?: Record; outputId: string; + /** + * Alternative output id that's reused when the output is updated. + */ + alternativeOutputId: string; onDidChangeData: Event; replaceData(items: IOutputDto): void; appendData(items: IOutputItemDto[]): void; From c4b5dff6088ecaffbb8d3f0bdcf26d937bd4c117 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Aug 2023 09:35:08 +0200 Subject: [PATCH 124/607] fix #189798 (#191090) --- .../preferences/browser/settingsTreeModels.ts | 2 +- .../browser/configurationService.ts | 2 +- .../test/browser/configurationService.test.ts | 16 ++++++++++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index bcd2604c8d7..180f990b5a5 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -273,7 +273,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { } private getTargetToInspect(setting: ISetting): SettingsTarget { - if (!this.userDataProfileService.currentProfile.isDefault) { + if (!this.userDataProfileService.currentProfile.isDefault && !this.userDataProfileService.currentProfile.useDefaultFlags?.settings) { if (setting.scope === ConfigurationScope.APPLICATION) { return ConfigurationTarget.APPLICATION; } diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index bc40358f62f..d2c05378c9c 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -47,7 +47,7 @@ import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/envir import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; function getLocalUserConfigurationScopes(userDataProfile: IUserDataProfile, hasRemote: boolean): ConfigurationScope[] | undefined { - return userDataProfile.isDefault + return (userDataProfile.isDefault || userDataProfile.useDefaultFlags?.settings) ? hasRemote ? LOCAL_MACHINE_SCOPES : undefined : hasRemote ? LOCAL_MACHINE_PROFILE_SCOPES : PROFILE_SCOPES; } 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 e0613cac0c7..72084f75018 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -1774,6 +1774,22 @@ suite('WorkspaceConfigurationService - Profiles', () => { assert.strictEqual(testObject.getValue('configurationService.profiles.testSetting'), 'profileValue2'); })); + test('switch to non default profile using settings from default profile', () => runWithFakedTimers({ useFakeTimers: true }, async () => { + await fileService.writeFile(instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString('{ "configurationService.profiles.applicationSetting": "applicationValue", "configurationService.profiles.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.profiles.applicationSetting": "profileValue", "configurationService.profiles.testSetting": "profileValue" }')); + await testObject.reloadConfiguration(); + + const profile = toUserDataProfile('custom3', 'custom3', joinPath(environmentService.userRoamingDataHome, 'profiles', 'custom2'), joinPath(environmentService.cacheHome, 'profilesCache'), { useDefaultFlags: { settings: true } }, instantiationService.get(IUserDataProfilesService).defaultProfile); + await fileService.writeFile(profile.settingsResource, VSBuffer.fromString('{ "configurationService.profiles.applicationSetting": "applicationValue2", "configurationService.profiles.testSetting": "profileValue2" }')); + const promise = Event.toPromise(testObject.onDidChangeConfiguration); + await userDataProfileService.updateCurrentProfile(profile); + + const changeEvent = await promise; + assert.deepStrictEqual([...changeEvent.affectedKeys], ['configurationService.profiles.applicationSetting', 'configurationService.profiles.testSetting']); + assert.strictEqual(testObject.getValue('configurationService.profiles.applicationSetting'), 'applicationValue2'); + assert.strictEqual(testObject.getValue('configurationService.profiles.testSetting'), 'profileValue2'); + })); + test('In non-default profile, changing application settings shall include only application scope settings in the change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { await fileService.writeFile(instantiationService.get(IUserDataProfilesService).defaultProfile.settingsResource, VSBuffer.fromString('{}')); await testObject.reloadConfiguration(); From 42ff46c8806112de0d04ef92c8fbd7ffaf820055 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Aug 2023 10:35:07 +0200 Subject: [PATCH 125/607] Hovering over an editor tab will "drop a white shadow" on the text (fix #189625) (#191165) --- .../workbench/browser/parts/editor/media/tabstitlecontrol.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 398e3058b2e..17e17c1a758 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -297,6 +297,10 @@ padding-right: 5px; /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ } +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.tab-actions-left):not(.tab-actions-off) .tab-label { + padding-right: 5px; /* ensure that the gradient does not show when tab actions show https://github.com/microsoft/vscode/issues/189625*/ +} + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky-compact:not(.has-icon) .monaco-icon-label { text-align: center; /* ensure that sticky-compact tabs without icon have label centered */ } From 7ef754c2f623aa662526e611c67e8e1a97e6753a Mon Sep 17 00:00:00 2001 From: Alpha Romer Coma <400829150120@r3-1.deped.gov.ph> Date: Thu, 24 Aug 2023 08:37:25 +0000 Subject: [PATCH 126/607] Fix supported markdown-lint violations in markdown files (#190750) docs: fix supported markdownlint violations --- .devcontainer/README.md | 5 +- .devcontainer/prebuilt/README.md | 45 +++++++++-------- CONTRIBUTING.md | 2 +- README.md | 8 ++-- SECURITY.md | 14 +++--- build/monaco/README-npm.md | 1 + extensions/git-base/README.md | 9 ++-- extensions/git/README.md | 8 ++-- extensions/javascript/syntaxes/Readme.md | 2 + extensions/json-language-features/README.md | 2 +- .../json-language-features/server/README.md | 29 +++++++---- .../markdown-language-features/README.md | 2 +- .../server/README.md | 46 ++++++++---------- extensions/media-preview/README.md | 1 - extensions/npm/README.md | 4 +- extensions/php-language-features/README.md | 2 +- extensions/simple-browser/README.md | 3 +- .../typescript-basics/syntaxes/Readme.md | 1 + .../web/README.md | 48 ++++++++++--------- .../test/colorize-fixtures/test.md | 2 +- src/vscode-dts/README.md | 9 ++-- test/README.md | 1 + test/integration/browser/README.md | 2 +- test/monaco/README.md | 8 ++-- test/smoke/Audit.md | 12 +++-- test/smoke/README.md | 4 +- test/unit/README.md | 4 +- 27 files changed, 145 insertions(+), 129 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 6522e98aac9..a5bde90d527 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -19,13 +19,14 @@ This dev container includes configuration for a development container for workin > **Note:** The Dev Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. 4. Due to the size of the repository we strongly recommend cloning it on a Linux filesystem for better bind mount performance. On macOS we recommend using a Docker volume (press F1 and select **Dev Containers: Clone Repository in Container Volume...**) and on Windows we recommend using a WSL folder: + - Make sure you are running a recent WSL version to get X11 and Wayland support. - Use the WSL extension for VS Code to open the cloned folder in WSL. - Press F1 and select **Dev Containers: Reopen in Container**. Next: **[Try it out!](#try-it)** -## Try it! +## Try it To start working with Code - OSS, follow these steps: @@ -50,6 +51,6 @@ Next, let's try debugging. Enjoy! -# Notes +## Notes The container comes with VS Code Insiders installed. To run it from an Integrated Terminal use `VSCODE_IPC_HOOK_CLI= /usr/bin/code-insiders .`. diff --git a/.devcontainer/prebuilt/README.md b/.devcontainer/prebuilt/README.md index 82e731230c0..2ca4619ce13 100644 --- a/.devcontainer/prebuilt/README.md +++ b/.devcontainer/prebuilt/README.md @@ -14,21 +14,21 @@ If you already have VS Code and Docker installed, you can click the badge above 2. **Important**: Docker needs at least **4 Cores and 8 GB of RAM** to run a full build with **9 GB of RAM** being recommended. If you are on macOS, or are using the old Hyper-V engine for Windows, update these values for Docker Desktop by right-clicking on the Docker status bar item and going to **Preferences/Settings > Resources > Advanced**. - > **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar. + > **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar. 3. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Dev Containers](https://aka.ms/vscode-remote/download/containers) extension. - ![Image of Dev Containers extension](https://microsoft.github.io/vscode-remote-release/images/dev-containers-extn.png) + ![Image of Dev Containers extension](https://microsoft.github.io/vscode-remote-release/images/dev-containers-extn.png) - > **Note:** The Dev Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. + > **Note:** The Dev Containers extension requires the Visual Studio Code distribution of Code - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. 4. Press Ctrl/Cmd + Shift + P or F1 and select **Dev Containers: Clone Repository in Container Volume...**. - > **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or when using the Hyper-V engine on Windows. We recommend the "clone repository in container" approach instead since it uses "named volume" rather than the local filesystem. + > **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or when using the Hyper-V engine on Windows. We recommend the "clone repository in container" approach instead since it uses "named volume" rather than the local filesystem. 5. Type `https://github.com/microsoft/vscode` (or a branch or PR URL) in the input box and press Enter. -6. After the container is running, open a web browser and go to [http://localhost:6080](http://localhost:6080), or use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password. +6. After the container is running, open a web browser and go to [http://localhost:6080](http://localhost:6080), or use a [VNC Viewer][def] to connect to `localhost:5901` and enter `vscode` as the password. Anything you start in VS Code, or the integrated terminal, will appear here. @@ -54,41 +54,42 @@ Next: **[Try it out!](#try-it)** ### Using VS Code with GitHub Codespaces -You may see improved VNC responsiveness when accessing a codespace from VS Code client since you can use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/). Here's how to do it. +You may see improved VNC responsiveness when accessing a codespace from VS Code client since you can use a [VNC Viewer][def]. Here's how to do it. -1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the the [GitHub Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces). +1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the the [GitHub Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces). - > **Note:** The GitHub Codespaces extension requires the Visual Studio Code distribution of Code - OSS. + > **Note:** The GitHub Codespaces extension requires the Visual Studio Code distribution of Code - OSS. 2. After the VS Code is up and running, press Ctrl/Cmd + Shift + P or F1, choose **Codespaces: Create New Codespace**, and use the following settings: - - `microsoft/vscode` for the repository. - - Select any branch (e.g. **main**) - you can select a different one later. - - Choose **Standard** (4-core, 8GB) as the size. -4. After you have connected to the codespace, you can use a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to `localhost:5901` and enter `vscode` as the password. +- `microsoft/vscode` for the repository. +- Select any branch (e.g. **main**) - you can select a different one later. +- Choose **Standard** (4-core, 8GB) as the size. + +3. After you have connected to the codespace, you can use a [VNC Viewer][def] to connect to `localhost:5901` and enter `vscode` as the password. > **Tip:** You may also need change your VNC client's **Picture Quality** setting to **High** to get a full color desktop. -5. Anything you start in VS Code, or the integrated terminal, will appear here. +4. Anything you start in VS Code, or the integrated terminal, will appear here. Next: **[Try it out!](#try-it)** -## Try it! +## Try it This container uses the [Fluxbox](http://fluxbox.org/) window manager to keep things lean. **Right-click on the desktop** to see menu options. It works with GNOME and GTK applications, so other tools can be installed if needed. -> **Note:** You can also set the resolution from the command line by typing `set-resolution`. + > **Note:** You can also set the resolution from the command line by typing `set-resolution`. To start working with Code - OSS, follow these steps: 1. In your local VS Code client, open a terminal (Ctrl/Cmd + Shift + \`) and type the following commands: - ```bash - yarn install - bash scripts/code.sh - ``` + ```bash + yarn install + bash scripts/code.sh + ``` -2. After the build is complete, open a web browser or a [VNC Viewer](https://www.realvnc.com/en/connect/download/viewer/) to connect to the desktop environment as described in the quick start and enter `vscode` as the password. +2. After the build is complete, open a web browser or a [VNC Viewer][def] to connect to the desktop environment as described in the quick start and enter `vscode` as the password. 3. You should now see Code - OSS! @@ -98,8 +99,10 @@ Next, let's try debugging. 2. Go to your local VS Code client, and use the **Run / Debug** view to launch the **VS Code** configuration. (Typically the default, so you can likely just press F5). - > **Note:** If launching times out, you can increase the value of `timeout` in the "VS Code", "Attach Main Process", "Attach Extension Host", and "Attach to Shared Process" configurations in [launch.json](../../.vscode/launch.json). However, running `scripts/code.sh` first will set up Electron which will usually solve timeout issues. + > **Note:** If launching times out, you can increase the value of `timeout` in the "VS Code", "Attach Main Process", "Attach Extension Host", and "Attach to Shared Process" configurations in [launch.json](../../.vscode/launch.json). However, running `scripts/code.sh` first will set up Electron which will usually solve timeout issues. 3. After a bit, Code - OSS will appear with the debugger attached! Enjoy! + +[def]: https://www.realvnc.com/en/connect/download/viewer/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b96e077aa67..f17fa843645 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -104,6 +104,6 @@ If you believe the bot got something wrong, please open a new issue and let us k If you are interested in writing code to fix issues, please see [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) in the wiki. -# Thank You! +## Thank You Your contributions to open source, large or small, make great projects like this possible. Thank you for taking the time to contribute. diff --git a/README.md b/README.md index 0c7c6236c42..61df8fc6bb4 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Visual Studio Code - Open Source ("Code - OSS") + [![Feature Requests](https://img.shields.io/github/issues/microsoft/vscode/feature-request.svg)](https://github.com/microsoft/vscode/issues?q=is%3Aopen+is%3Aissue+label%3Afeature-request+sort%3Areactions-%2B1-desc) [![Bugs](https://img.shields.io/github/issues/microsoft/vscode/bug.svg)](https://github.com/microsoft/vscode/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3Abug) [![Gitter](https://img.shields.io/badge/chat-on%20gitter-yellow.svg)](https://gitter.im/Microsoft/vscode) @@ -60,9 +61,10 @@ VS Code includes a set of built-in extensions located in the [extensions](extens This repository includes a Visual Studio Code Dev Containers / GitHub Codespaces development container. -- For [Dev Containers](https://aka.ms/vscode-remote/download/containers), use the **Dev Containers: Clone Repository in Container Volume...** command which creates a Docker volume for better disk I/O on macOS and Windows. - - If you already have VS Code and Docker installed, you can also click [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) to get started. This will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use. -- For Codespaces, install the [GitHub Codespaces](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces) extension in VS Code, and use the **Codespaces: Create New Codespace** command. +* For [Dev Containers](https://aka.ms/vscode-remote/download/containers), use the **Dev Containers: Clone Repository in Container Volume...** command which creates a Docker volume for better disk I/O on macOS and Windows. + * If you already have VS Code and Docker installed, you can also click [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) to get started. This will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use. + +* For Codespaces, install the [GitHub Codespaces](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces) extension in VS Code, and use the **Codespaces: Create New Codespace** command. Docker / the Codespace should have at least **4 Cores and 6 GB of RAM (8 GB recommended)** to run full build. See the [development container README](.devcontainer/README.md) for more information. diff --git a/SECURITY.md b/SECURITY.md index a050f362c15..4fa5946a867 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -18,13 +18,13 @@ You should receive a response within 24 hours. If for some reason you do not, pl Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue +* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) +* Full paths of source file(s) related to the manifestation of the issue +* The location of the affected source code (tag/branch/commit or direct URL) +* Any special configuration required to reproduce the issue +* Step-by-step instructions to reproduce the issue +* Proof-of-concept or exploit code (if possible) +* Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. diff --git a/build/monaco/README-npm.md b/build/monaco/README-npm.md index ca5592e0fe1..ec8eb5a4037 100644 --- a/build/monaco/README-npm.md +++ b/build/monaco/README-npm.md @@ -10,4 +10,5 @@ The Monaco Editor is the code editor that powers [VS Code](https://github.com/mi This npm module contains the core editor functionality, as it comes from the [vscode repository](https://github.com/microsoft/vscode). ## License + [MIT](https://github.com/microsoft/vscode/blob/main/LICENSE.txt) diff --git a/extensions/git-base/README.md b/extensions/git-base/README.md index ff5bcc321c7..d6f0b7c128b 100644 --- a/extensions/git-base/README.md +++ b/extensions/git-base/README.md @@ -14,7 +14,8 @@ The Git extension exposes an API, reachable by any other extension. 2. Include `git-base.d.ts` in your extension's compilation. 3. Get a hold of the API with the following snippet: - ```ts - const gitBaseExtension = vscode.extensions.getExtension('vscode.git-base').exports; - const git = gitBaseExtension.getAPI(1); - ``` + ```ts + const gitBaseExtension = vscode.extensions.getExtension('vscode.git-base').exports; + const git = gitBaseExtension.getAPI(1); + + ``` diff --git a/extensions/git/README.md b/extensions/git/README.md index a20f3207534..2a6678de933 100644 --- a/extensions/git/README.md +++ b/extensions/git/README.md @@ -14,7 +14,7 @@ The Git extension exposes an API, reachable by any other extension. 2. Include `git.d.ts` in your extension's compilation. 3. Get a hold of the API with the following snippet: - ```ts - const gitExtension = vscode.extensions.getExtension('vscode.git').exports; - const git = gitExtension.getAPI(1); - ``` \ No newline at end of file + ```ts + const gitExtension = vscode.extensions.getExtension('vscode.git').exports; + const git = gitExtension.getAPI(1); + ``` diff --git a/extensions/javascript/syntaxes/Readme.md b/extensions/javascript/syntaxes/Readme.md index bc29199fd73..b7db3a6a4c9 100644 --- a/extensions/javascript/syntaxes/Readme.md +++ b/extensions/javascript/syntaxes/Readme.md @@ -1,10 +1,12 @@ The file `JavaScript.tmLanguage.json` is derived from [TypeScriptReact.tmLanguage](https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage). To update to the latest version: + - `cd extensions/typescript` and run `npm run update-grammars` - don't forget to run the integration tests at `./scripts/test-integration.sh` The script does the following changes: + - fileTypes .tsx -> .js & .jsx - scopeName scope.tsx -> scope.js - update all rule names .tsx -> .js diff --git a/extensions/json-language-features/README.md b/extensions/json-language-features/README.md index 2ff5e6e57d3..3de3d11081a 100644 --- a/extensions/json-language-features/README.md +++ b/extensions/json-language-features/README.md @@ -4,4 +4,4 @@ ## Features -See [JSON in Visual Studio Code](https://code.visualstudio.com/docs/languages/json) to learn about the features of this extension. \ No newline at end of file +See [JSON in Visual Studio Code](https://code.visualstudio.com/docs/languages/json) to learn about the features of this extension. diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index e9875ba5977..10956439e32 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -11,6 +11,7 @@ The JSON Language server provides language-specific smarts for editing, validati ### Server capabilities The JSON language server supports requests on documents of language id `json` and `jsonc`. + - `json` documents are parsed and validated following the [JSON specification](https://tools.ietf.org/html/rfc7159). - `jsonc` documents additionally accept single line (`//`) and multi-line comments (`/* ... */`). JSONC is a VSCode specific file format, intended for VSCode configuration files, without any aspirations to define a new common file format. @@ -25,12 +26,12 @@ The server implements the following capabilities of the language server protocol - Semantic Selection for semantic selection for one or multiple cursor positions. - [Goto Definition](https://microsoft.github.io/language-server-protocol/specification#textDocument_definition) for $ref references in JSON schemas - [Diagnostics (Validation)](https://microsoft.github.io/language-server-protocol/specification#textDocument_publishDiagnostics) are pushed for all open documents - - syntax errors - - structural validation based on the document's [JSON schema](http://json-schema.org/). + - syntax errors + - structural validation based on the document's [JSON schema](http://json-schema.org/). In order to load JSON schemas, the JSON server uses NodeJS `http` and `fs` modules. For all other features, the JSON server only relies on the documents and settings provided by the client through the LSP. -### Client requirements: +### Client requirements The JSON language server expects the client to only send requests and notifications for documents of language id `json` and `jsonc`. @@ -56,8 +57,8 @@ Clients may send a `workspace/didChangeConfiguration` notification to notify the The server supports the following settings: - http - - `proxy`: The URL of the proxy server to use when fetching schema. When undefined or empty, no proxy is used. - - `proxyStrictSSL`: Whether the proxy server certificate should be verified against the list of supplied CAs. + - `proxy`: The URL of the proxy server to use when fetching schema. When undefined or empty, no proxy is used. + - `proxyStrictSSL`: Whether the proxy server certificate should be verified against the list of supplied CAs. - json - `format` @@ -72,6 +73,7 @@ The server supports the following settings: - `resultLimit`: The max number of color decorators and outline symbols to be computed (for performance reasons) - `jsonFoldingLimit`: The max number of folding ranges to be computed for json documents (for performance reasons) - `jsoncFoldingLimit`: The max number of folding ranges to be computed for jsonc documents (for performance reasons) + ```json { "http": { @@ -103,6 +105,7 @@ The server supports the following settings: [JSON schemas](http://json-schema.org/) are essential for code assist, hovers, color decorators to work and are required for structural validation. To find the schema for a given JSON document, the server uses the following mechanisms: + - JSON documents can define the schema URL using a `$schema` property - The settings define a schema association based on the documents URL. Settings can either associate a schema URL to a file or path pattern, and they can directly provide a schema. - Additionally, schema associations can also be provided by a custom 'schemaAssociations' configuration call. @@ -115,9 +118,9 @@ The `initializationOptions.handledSchemaProtocols` initialization option defines ```ts let clientOptions: LanguageClientOptions = { - initializationOptions: { - handledSchemaProtocols: ['file'] // language server should only try to load file URLs - } + initializationOptions: { + handledSchemaProtocols: ['file'] // language server should only try to load file URLs + } ... } ``` @@ -132,6 +135,7 @@ If `handledSchemaProtocols` is not set, the JSON language server will load the f Requests for schemas with URLs not handled by the server are forwarded to the client through an LSP request. This request is a JSON language server-specific, non-standardized, extension to the LSP. Request: + - method: 'vscode/content' - params: `string` - The schema URL to request. - response: `string` - The content of the schema with the given URL @@ -146,6 +150,7 @@ The server will, as a response, clear the schema content from the cache and relo In addition to the settings, schemas associations can also be provided through a notification from the client to the server. This notification is a JSON language server-specific, non-standardized, extension to the LSP. Notification: + - method: 'json/schemaAssociations' - params: `ISchemaAssociations` or `ISchemaAssociation[]` defined as follows @@ -183,11 +188,14 @@ interface ISchemaAssociation { } ``` + `ISchemaAssociations` - - keys: a file names or file path (separated by `/`). `*` can be used as a wildcard. - - values: An array of schema URLs + +- keys: a file names or file path (separated by `/`). `*` can be used as a wildcard. +- values: An array of schema URLs Notification: + - method: 'json/schemaContent' - params: `string` the URL of the schema that has changed. @@ -226,6 +234,7 @@ The source code of the JSON language server can be found in the [VSCode reposito File issues and pull requests in the [VSCode GitHub Issues](https://github.com/microsoft/vscode/issues). See the document [How to Contribute](https://github.com/microsoft/vscode/wiki/How-to-Contribute) on how to build and run from source. Most of the functionality of the server is located in libraries: + - [jsonc-parser](https://github.com/microsoft/node-jsonc-parser) contains the JSON parser and scanner. - [vscode-json-languageservice](https://github.com/microsoft/vscode-json-languageservice) contains the implementation of all features as a re-usable library. - [vscode-languageserver-node](https://github.com/microsoft/vscode-languageserver-node) contains the implementation of language server for NodeJS. diff --git a/extensions/markdown-language-features/README.md b/extensions/markdown-language-features/README.md index e80e9e886bb..2052521da84 100644 --- a/extensions/markdown-language-features/README.md +++ b/extensions/markdown-language-features/README.md @@ -4,4 +4,4 @@ ## Features -See [Markdown in Visual Studio Code](https://code.visualstudio.com/docs/languages/markdown) to learn about the features of this extension. \ No newline at end of file +See [Markdown in Visual Studio Code](https://code.visualstudio.com/docs/languages/markdown) to learn about the features of this extension. diff --git a/extensions/markdown-language-features/server/README.md b/extensions/markdown-language-features/server/README.md index 1fd38302195..4114d2698bf 100644 --- a/extensions/markdown-language-features/server/README.md +++ b/extensions/markdown-language-features/server/README.md @@ -6,7 +6,6 @@ The Markdown language server powers VS Code's built-in markdown support, providi This server uses the [Markdown Language Service](https://github.com/microsoft/vscode-markdown-languageservice) to implement almost all of the language features. You can use that library if you need a library for working with Markdown instead of a full language server. - ## Server capabilities - [Completions](https://microsoft.github.io/language-server-protocol/specification#textDocument_completion) for Markdown links. @@ -31,14 +30,13 @@ This server uses the [Markdown Language Service](https://github.com/microsoft/vs - [Code Actions](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_codeAction) - - Organize link definitions source action. - - Extract link to definition refactoring. + - Organize link definitions source action. + - Extract link to definition refactoring. - Updating links when a file is moved / renamed. Uses a custom `markdown/getEditForFileRenames` message. - [Pull diagnostics (validation)](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_pullDiagnostics) for links. - ## Client requirements ### Initialization options @@ -53,27 +51,27 @@ Clients may send a `workspace/didChangeConfiguration` notification to notify the The server supports the following settings: - `markdown` - - `suggest` - - `paths` - - `enabled` — Enable/disable path suggestions. + - `suggest` + - `paths` + - `enabled` — Enable/disable path suggestions. - - `occurrencesHighlight` - - `enabled` — Enable/disable highlighting of link occurrences. + - `occurrencesHighlight` + - `enabled` — Enable/disable highlighting of link occurrences. - - `validate` - - `enabled` — Enable/disable all validation. - - `referenceLinks` - - `enabled` — Enable/disable validation of reference links: `[text][ref]` - - `fragmentLinks` - - `enabled` — Enable/disable validation of links to fragments in the current files: `[text](#head)` - - `fileLinks` - - `enabled` — Enable/disable validation of links to file in the workspace. - - `markdownFragmentLinks` — Enable/disable validation of links to headers in other Markdown files. Use `inherit` to inherit the `fragmentLinks` setting. - - `ignoredLinks` — Array of glob patterns for files that should not be validated. - - `unusedLinkDefinitions` - - `enabled` — Enable/disable validation of unused link definitions. - - `duplicateLinkDefinitions` - - `enabled` — Enable/disable validation of duplicated link definitions. + - `validate` + - `enabled` — Enable/disable all validation. + - `referenceLinks` + - `enabled` — Enable/disable validation of reference links: `[text][ref]` + - `fragmentLinks` + - `enabled` — Enable/disable validation of links to fragments in the current files: `[text](#head)` + - `fileLinks` + - `enabled` — Enable/disable validation of links to file in the workspace. + - `markdownFragmentLinks` — Enable/disable validation of links to headers in other Markdown files. Use `inherit` to inherit the `fragmentLinks` setting. + - `ignoredLinks` — Array of glob patterns for files that should not be validated. + - `unusedLinkDefinitions` + - `enabled` — Enable/disable validation of unused link definitions. + - `duplicateLinkDefinitions` + - `enabled` — Enable/disable validation of duplicated link definitions. ### Custom requests @@ -109,7 +107,6 @@ Delete a previously created file watcher. Get a list of all markdown files in the workspace. - ## Contribute The source code of the Markdown language server can be found in the [VSCode repository](https://github.com/microsoft/vscode) at [extensions/markdown-language-features/server](https://github.com/microsoft/vscode/tree/master/extensions/markdown-language-features/server). @@ -132,4 +129,3 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the [MIT](https://github.com/microsoft/vscode/blob/master/LICENSE.txt) License. - diff --git a/extensions/media-preview/README.md b/extensions/media-preview/README.md index 48428a684bf..8163e017143 100644 --- a/extensions/media-preview/README.md +++ b/extensions/media-preview/README.md @@ -16,7 +16,6 @@ This extension provides basic preview for images, audio and video files. - `.webp` - `.avif` - ### Supported audio formats - `.mp3` diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 82730c7e82a..296bf03f73e 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -28,7 +28,7 @@ The extension supports running a script as a task from a folder in the Explorer. ### Others -The extension fetches data from https://registry.npmjs.org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies. +The extension fetches data from and to provide auto-completion and information on hover features on npm dependencies. ## Settings @@ -40,5 +40,3 @@ The extension fetches data from https://registry.npmjs.org and https://registry. - `npm.scriptExplorerAction` - The default click action: `open` or `run`, the default is `open`. - `npm.enableRunFromFolder` - Enable running npm scripts from the context menu of folders in Explorer, the default is `false`. - `npm.scriptCodeLens.enable` - Enable/disable the code lenses to run a script, the default is `false`. - - diff --git a/extensions/php-language-features/README.md b/extensions/php-language-features/README.md index c00be6a964e..e0d28f5254f 100644 --- a/extensions/php-language-features/README.md +++ b/extensions/php-language-features/README.md @@ -4,4 +4,4 @@ ## Features -See [PHP in Visual Studio Code](https://code.visualstudio.com/docs/languages/php) to learn about the features of this extension. \ No newline at end of file +See [PHP in Visual Studio Code](https://code.visualstudio.com/docs/languages/php) to learn about the features of this extension. diff --git a/extensions/simple-browser/README.md b/extensions/simple-browser/README.md index b4ecf7a4ad6..5121dc86e89 100644 --- a/extensions/simple-browser/README.md +++ b/extensions/simple-browser/README.md @@ -2,5 +2,4 @@ **Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. -Provides a very basic browser preview using an iframe embedded in a [webview](). This extension is primarily meant to be used by other extensions for showing simple web content. - +Provides a very basic browser preview using an iframe embedded in a [webviewW](). This extension is primarily meant to be used by other extensions for showing simple web content. diff --git a/extensions/typescript-basics/syntaxes/Readme.md b/extensions/typescript-basics/syntaxes/Readme.md index 2f9c2b95ee2..fa05c28d970 100644 --- a/extensions/typescript-basics/syntaxes/Readme.md +++ b/extensions/typescript-basics/syntaxes/Readme.md @@ -1,6 +1,7 @@ The file `TypeScript.tmLanguage.json` and `TypeScriptReact.tmLanguage.json` are derived from [TypeScript.tmLanguage](https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScript.tmLanguage) and [TypeScriptReact.tmLanguage](https://github.com/microsoft/TypeScript-TmLanguage/blob/master/TypeScriptReact.tmLanguage). To update to the latest version: + - `cd extensions/typescript` and run `npm run update-grammars` - don't forget to run the integration tests at `./scripts/test-integration.sh` diff --git a/extensions/typescript-language-features/web/README.md b/extensions/typescript-language-features/web/README.md index 9cae35b8cf3..a9c19b5d72a 100644 --- a/extensions/typescript-language-features/web/README.md +++ b/extensions/typescript-language-features/web/README.md @@ -1,4 +1,5 @@ # vscode-wasm-typescript + Language server host for typescript using vscode's sync-api in the browser ## TODOs @@ -22,33 +23,33 @@ Language server host for typescript using vscode's sync-api in the browser - LATER: Turns out you can skip the existing server by depending on tsserverlibrary instead of tsserver. - [x] figure out a webpack-native way to generate tsserver.web.js if possible - [x] path rewriting is pretty loosey-goosey; likely to be incorrect some of the time - - invert the logic from TypeScriptServiceClient.normalizedPath for requests - - invert the function from webServer.ts for responses (maybe) - - something with getWorkspaceRootForResource (or anything else that checks `resouce.scheme`) + - invert the logic from TypeScriptServiceClient.normalizedPath for requests + - invert the function from webServer.ts for responses (maybe) + - something with getWorkspaceRootForResource (or anything else that checks `resouce.scheme`) - [x] put files one level down from virtual root - [x] fill in missing environment files like lib.dom.d.ts - - toResource's isWeb branch *probably* knows where to find this, just need to put it in the virtual FS - - I guess during setup in serverProcess.browser.ts. - - Not sure whether it needs to have the data or just a fs entry. - - Wait, I don't know how files get added to the FS normally. + - toResource's isWeb branch *probably* knows where to find this, just need to put it in the virtual FS + - I guess during setup in serverProcess.browser.ts. + - Not sure whether it needs to have the data or just a fs entry. + - Wait, I don't know how files get added to the FS normally. - [x] cancellation should only retain one cancellation checker - - the one that matches the current request id - - but that means tracking (or retrieving from tsserver) the request id (aka seq?) - - and correctly setting/resetting it on the cancellation token too. - - I looked at the tsserver code. I think the web case is close to the single-pipe node case, + - the one that matches the current request id + - but that means tracking (or retrieving from tsserver) the request id (aka seq?) + - and correctly setting/resetting it on the cancellation token too. + - I looked at the tsserver code. I think the web case is close to the single-pipe node case, so I just require that requestId is set in order to call the *current* cancellation checker. - - Any incoming message with a cancellation checker will overwrite the current one. + - Any incoming message with a cancellation checker will overwrite the current one. - [x] Cancellation code in vscode is suspiciously prototypey. - - Specifically, it adds the vscode-wasm cancellation to original cancellation code, but should actually switch to the former for web only. - - looks like `isWeb()` is a way to check for being on the web + - Specifically, it adds the vscode-wasm cancellation to original cancellation code, but should actually switch to the former for web only. + - looks like `isWeb()` is a way to check for being on the web - [x] create multiple watchers - - on-demand instead of watching everything and checking on watch firing + - on-demand instead of watching everything and checking on watch firing - [x] get file watching to work - - it could *already* work, I just don't know how to test it - - look at extensions/markdown-language-features/src/client/fileWatchingManager.ts to see if I can use that - - later: it is OK. its main difference is that you can watch files in not-yet-created directories, and it maintains + - it could *already* work, I just don't know how to test it + - look at extensions/markdown-language-features/src/client/fileWatchingManager.ts to see if I can use that + - later: it is OK. its main difference is that you can watch files in not-yet-created directories, and it maintains a web of directory watches that then check whether the file is eventually created. - - even later: well, it works even though it is similar to my code. + - even later: well, it works even though it is similar to my code. I'm not sure what is different. - [x] copy fileWatchingManager.ts to web/ ; there's no sharing code between extensions - [x] Find out scheme the web actually uses instead of vscode-test-web (or switch over entirely to isWeb) @@ -106,6 +107,7 @@ Language server host for typescript using vscode's sync-api in the browser - so I can just redo whatever that did and it'll be fine ### Done + - [x] need to update 0.2 -> 0.7.* API (once it's working properly) - [x] including reshuffling the webpack hack if needed - [x] need to use the settings recommended by Sheetal @@ -113,7 +115,7 @@ Language server host for typescript using vscode's sync-api in the browser - [x] sync-api-client says fs is rooted at memfs:/sample-folder; the protocol 'memfs:' is confusing our file parsing I think - [x] nothing ever seems to find tsconfig.json - [x] messages aren't actually coming through, just the message from the first request - - fixed by simplifying the listener setup for now + - fixed by simplifying the listener setup for now - [x] once messages work, you can probably log by postMessage({ type: 'log', body: "some logging text" }) - [x] implement realpath, modifiedtime, resolvepath, then turn semantic mode on - [x] file watching implemented with saved map of filename to callback, and forwarding @@ -125,6 +127,7 @@ Language server host for typescript using vscode's sync-api in the browser ## Notes messages received by extension AND host use paths like ^/memfs/ts-nul-authority/sample-folder/file.ts + - problem: pretty sure the extension doesn't know what to do with that: it's not putting down error spans in file.ts - question: why is the extension requesting quickinfo in that URI format? And it works! (probably because the result is a tooltip, not an in-file span) - problem: weird concatenations with memfs:/ in the middle @@ -140,15 +143,14 @@ but readFile is getting called with things like memfs:/sample-folder/memfs:/type watchDirectory with /sample-folder/^ and directoryExists with /sample-folder/^/memfs/ts-nul-authority/sample-folder/workspaces/ watchFile with /sample-folder/memfs:/sample-folder/memfs:/lib.es2020.full.d.ts -### LATER: +### LATER OK, so the paths that tsserver has look like this: ^/scheme/mount/whatever.ts but the paths the filesystem has look like this: scheme:/whatever.ts (not sure about 'mount', that's only when cloning from the fs) so you have to shave off the scheme that the host combined with the path and put on the scheme that the vfs is using. -### LATER 2: +### LATER 2 Some commands ask for getExecutingFilePath or getCurrentDirectory and cons up a path themselves. This works, because URI.from({ scheme, path }) matches what the fs has in it Problem: In *some* messages (all?), vscode then refers to /x.ts and ^/vscode-test-web/mount/x.ts (or ^/memfs/ts-nul-authority/x.ts) - diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/test.md b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.md index 28f3590536e..309aa6de793 100644 --- a/extensions/vscode-colorize-tests/test/colorize-fixtures/test.md +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/test.md @@ -103,4 +103,4 @@ Pop * Multiple definitions and terms are possible * Definitions can include multiple paragraphs too -*[ABBR]: Markdown plus abbreviations (produces an tag) \ No newline at end of file +*[ABBR]: Markdown plus abbreviations (produces an tag) diff --git a/src/vscode-dts/README.md b/src/vscode-dts/README.md index a69e5eb65e1..9b3640d9208 100644 --- a/src/vscode-dts/README.md +++ b/src/vscode-dts/README.md @@ -1,18 +1,17 @@ -## vscode-dts +# vscode-dts This is the place for the stable API and for API proposals. - -### Consume a proposal +## Consume a proposal 1. find a proposal you are interested in 1. add its name to your extensions `package.json#enabledApiProposals` property 1. run `npx vscode-dts dev` to download the `d.ts` files into your project 1. don't forget that extension using proposed API cannot be published -1. learn more here: https://code.visualstudio.com/api/advanced-topics/using-proposed-api +1. learn more here: -### Add a new proposal +## Add a new proposal 1. create a _new_ file in this directory, its name must follow this pattern `vscode.proposed.[a-zA-Z]+.d.ts` 1. creating the proposal-file will automatically update `src/vs/workbench/services/extensions/common/extensionsApiProposals.ts` (make sure to run `yarn watch`) diff --git a/test/README.md b/test/README.md index 80cf9d92912..1e4d114dce5 100644 --- a/test/README.md +++ b/test/README.md @@ -3,6 +3,7 @@ ## Contents This folder contains the various test runners for VSCode. Please refer to the documentation within for how to run them: + * `unit`: our suite of unit tests ([README](unit/README.md)) * `integration`: our suite of API tests ([README](integration/browser/README.md)) * `smoke`: our suite of automated UI tests ([README](smoke/README.md)) diff --git a/test/integration/browser/README.md b/test/integration/browser/README.md index 34107241f8e..8b25994564d 100644 --- a/test/integration/browser/README.md +++ b/test/integration/browser/README.md @@ -21,7 +21,7 @@ All integration tests run in a browser instance as specified by the command line Add the `--debug` flag to see a browser window with the tests running. -**Note**: you can enable verbose logging of playwright library by setting a `DEBUG` environment variable before running the tests (https://playwright.dev/docs/debug#verbose-api-logs) +**Note**: you can enable verbose logging of playwright library by setting a `DEBUG` environment variable before running the tests () ## Debug diff --git a/test/monaco/README.md b/test/monaco/README.md index 68bb7051ce8..e55338934f2 100644 --- a/test/monaco/README.md +++ b/test/monaco/README.md @@ -4,10 +4,10 @@ This directory contains scripts that are used to smoke test the Monaco Editor di ## Setup & Bundle - $test/monaco> yarn - $test/monaco> yarn run bundle + $test/monaco> yarn + $test/monaco> yarn run bundle ## Compile and run tests - $test/monaco> yarn run compile - $test/monaco> yarn test + $test/monaco> yarn run compile + $test/monaco> yarn test diff --git a/test/smoke/Audit.md b/test/smoke/Audit.md index 4ec76567e9c..fd8913b44e2 100644 --- a/test/smoke/Audit.md +++ b/test/smoke/Audit.md @@ -1,13 +1,15 @@ # VS Code Smoke Tests Failures History + This file contains a history of smoke test failures which could be avoided if particular techniques were used in the test (e.g. binding test elements with HTML5 `data-*` attribute). To better understand what can be employed in smoke test to ensure its stability, it is important to understand patterns that led to smoke test breakage. This markdown is a result of work on [this issue](https://github.com/microsoft/vscode/issues/27906). -# Log -1. This following change led to the smoke test failure because DOM element's attribute `a[title]` was changed: - [eac49a3](https://github.com/microsoft/vscode/commit/eac49a321b84cb9828430e9dcd3f34243a3480f7) +## Log - This attribute was used in the smoke test to grab the contents of SCM part in status bar: - [0aec2d6](https://github.com/microsoft/vscode/commit/0aec2d6838b5e65cc74c33b853ffbd9fa191d636) +1. This following change led to the smoke test failure because DOM element's attribute `a[title]` was changed: + [eac49a3](https://github.com/microsoft/vscode/commit/eac49a321b84cb9828430e9dcd3f34243a3480f7) + + This attribute was used in the smoke test to grab the contents of SCM part in status bar: + [0aec2d6](https://github.com/microsoft/vscode/commit/0aec2d6838b5e65cc74c33b853ffbd9fa191d636) 2. To be continued... diff --git a/test/smoke/README.md b/test/smoke/README.md index ffef4c28339..9b5eb6282b4 100644 --- a/test/smoke/README.md +++ b/test/smoke/README.md @@ -2,7 +2,7 @@ Make sure you are on **Node v12.x**. -### Quick Overview +## Quick Overview ```bash # Build extensions in the VS Code repo (if needed) @@ -57,7 +57,7 @@ xattr -d com.apple.quarantine - `-f PATTERN` (alias `-g PATTERN`) filters the tests to be run. You can also use pretty much any mocha argument; - `--headless` will run playwright in headless mode when `--web` is used. -**Note**: you can enable verbose logging of playwright library by setting a `DEBUG` environment variable before running the tests (https://playwright.dev/docs/debug#verbose-api-logs), for example to `pw:browser`. +**Note**: you can enable verbose logging of playwright library by setting a `DEBUG` environment variable before running the tests (), for example to `pw:browser`. ### Develop diff --git a/test/unit/README.md b/test/unit/README.md index 58d569d571f..154f1cf0ad0 100644 --- a/test/unit/README.md +++ b/test/unit/README.md @@ -33,10 +33,10 @@ Unit tests from layers `common` and `browser` are run inside `chromium`, `webkit The following command will create a `coverage` folder in the `.build` folder at the root of the workspace: -**OS X and Linux** +### OS X and Linux ./scripts/test.sh --coverage -**Windows** +### Windows scripts\test --coverage From ca86548968a4ea83a56cbe3b16a36888fbf41213 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 24 Aug 2023 10:57:12 +0200 Subject: [PATCH 127/607] changing from a js doc to a doc --- .../src/configuration/configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index 23b1ee8b466..d338792c58c 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -200,7 +200,7 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu } protected readEnableDiagnosticsTelemetry(configuration: vscode.WorkspaceConfiguration): boolean { - /** This setting does not appear in the settings view, as it is not to be enabled by users outside the team */ + // This setting does not appear in the settings view, as it is not to be enabled by users outside the team return configuration.get('typescript.enableDiagnosticsTelemetry', false); } From a597b9044f4891ab9c13e736a86ac66b747417ac Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 24 Aug 2023 11:01:47 +0200 Subject: [PATCH 128/607] disposing the telemetry emitter --- .../src/languageFeatures/diagnostics.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts index a2a889d1ba2..1f6c21a5d88 100644 --- a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts @@ -156,6 +156,7 @@ class DiagnosticsTelemetryManager extends Disposable { private readonly _diagnosticCodesMap = new Map(); private readonly _diagnosticSnapshotsMap = new ResourceMap(uri => uri.toString(), { onCaseInsensitiveFileSystem: false }); private _timeout: NodeJS.Timeout | undefined; + private _telemetryEmitter: NodeJS.Timer | undefined; constructor( private readonly _telemetryReporter: TelemetryReporter, @@ -196,7 +197,7 @@ class DiagnosticsTelemetryManager extends Disposable { } private _registerTelemetryEventEmitter() { - setInterval(() => { + this._telemetryEmitter = setInterval(() => { if (this._diagnosticCodesMap.size > 0) { let diagnosticCodes = ''; this._diagnosticCodesMap.forEach((value, key) => { @@ -222,6 +223,7 @@ class DiagnosticsTelemetryManager extends Disposable { override dispose() { super.dispose(); clearTimeout(this._timeout); + clearInterval(this._telemetryEmitter); } } From 76ab868e1c01ee0c01116bfe50b6d3e7ee098c88 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 24 Aug 2023 11:18:24 +0200 Subject: [PATCH 129/607] review comment --- .../browser/stickyScrollController.ts | 1 + .../browser/stickyScrollWidget.ts | 20 +++++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 5764622526d..53a7aa18f3c 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -430,6 +430,7 @@ export class StickyScrollController extends Disposable implements IEditorContrib private _renderStickyScroll() { const model = this._editor.getModel(); if (!model || model.isTooLargeForTokenization()) { + this._stickyScrollWidget.setState(undefined); return; } const stickyLineVersion = this._stickyLineCandidateProvider.getVersionId(); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 79e9f479add..607dcb73068 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -99,9 +99,11 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return this._lineNumbers; } - setState(state: StickyScrollWidgetState): void { - dom.clearNode(this._lineNumbersDomNode); - dom.clearNode(this._linesDomNode); + setState(state: StickyScrollWidgetState | undefined): void { + this._clearStickyWidget(); + if (!state) { + return; + } this._stickyLines = []; const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); const futureWidgetHeight = state.startLineNumbers.length * editorLineHeight + state.lastLineRelativePosition; @@ -129,6 +131,12 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.style.width = `${layoutInfo.width - layoutInfo.minimap.minimapCanvasOuterWidth - layoutInfo.verticalScrollbarWidth}px`; } + private _clearStickyWidget() { + dom.clearNode(this._lineNumbersDomNode); + dom.clearNode(this._linesDomNode); + this._rootDomNode.style.display = 'none'; + } + private _renderRootNode(): void { if (!this._editor._getViewModel()) { @@ -145,7 +153,11 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); const widgetHeight: number = this._lineNumbers.length * editorLineHeight + this._lastLineRelativePosition; - this._rootDomNode.style.display = widgetHeight > 0 ? 'block' : 'none'; + if (widgetHeight === 0) { + this._clearStickyWidget(); + return; + } + this._rootDomNode.style.display = 'block'; this._lineNumbersDomNode.style.height = `${widgetHeight}px`; this._linesDomNodeScrollable.style.height = `${widgetHeight}px`; this._rootDomNode.style.height = `${widgetHeight}px`; From 65068af4f74187ca06e763f93ddee46d10511002 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 11:28:16 +0200 Subject: [PATCH 130/607] Fixes #191144 --- .../browser/parts/editor/editorCommands.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 044bd29da9e..77c8985611a 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -361,6 +361,13 @@ function registerDiffEditorCommands(): void { handler: accessor => navigateInDiffEditor(accessor, true) }); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: GOTO_NEXT_CHANGE, + title: { value: localize('compare.nextChange', "Go to Next Change"), original: 'Go to Next Change' }, + } + }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: GOTO_PREVIOUS_CHANGE, weight: KeybindingWeight.WorkbenchContrib, @@ -369,6 +376,13 @@ function registerDiffEditorCommands(): void { handler: accessor => navigateInDiffEditor(accessor, false) }); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: GOTO_PREVIOUS_CHANGE, + title: { value: localize('compare.previousChange', "Go to Previous Change"), original: 'Go to Previous Change' }, + } + }); + function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined { const editorService = accessor.get(IEditorService); From eac8efd2cd1ff9dfa1bc40e7c24c7cafe20f4523 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Aug 2023 11:56:40 +0200 Subject: [PATCH 131/607] fix #189339 (#191187) --- src/vs/workbench/browser/parts/views/treeView.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index c5dc8bf57e3..62ca965951c 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -613,7 +613,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { // Pass Focus to Viewer this.tree.domFocus(); - } else if (this.tree) { + } else if (this.tree && this.treeContainer && !this.treeContainer.classList.contains('hide')) { this.tree.domFocus(); } else { this.domNode.focus(); @@ -1017,6 +1017,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this.domNode.setAttribute('tabindex', '0'); } else if (this.treeContainer) { this.treeContainer.classList.remove('hide'); + if (this.domNode === DOM.getActiveElement()) { + this.focus(); + } this.domNode.removeAttribute('tabindex'); } } From 681cd1b481c17b556123f3fafbd4bba8a3d366c3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 12:01:19 +0200 Subject: [PATCH 132/607] Fixes https://github.com/microsoft/vscode/issues/187889 --- .../inlineCompletions/browser/inlineCompletionsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 51b2d4fc562..094393e32c5 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -117,7 +117,7 @@ export class InlineCompletionsController extends Disposable { this._register(editor.onDidChangeCursorPosition(e => transaction(tx => { /** @description onDidChangeCursorPosition */ this.updateObservables(tx, VersionIdChangeReason.Other); - if (e.reason === CursorChangeReason.Explicit) { + if (e.reason === CursorChangeReason.Explicit || e.source === 'api') { this.model.get()?.stop(tx); } }))); From 2d0cb77ab8b4950f9331dbdbf71aacc305c91b9f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Aug 2023 12:45:53 +0200 Subject: [PATCH 133/607] voice - support inline chat --- .../browser/actions/chatExecuteActions.ts | 2 +- .../actions/chatVoiceInputActions.ts | 413 ++++++++++++------ .../browser/inlineChatController.ts | 14 +- .../workbenchVoiceRecognitionService.ts | 1 + 4 files changed, 290 insertions(+), 140 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index 7f639db3aeb..07776d570a6 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -17,7 +17,7 @@ export interface IChatExecuteActionContext { inputValue?: string; } -function isExecuteActionContext(thing: unknown): thing is IChatExecuteActionContext { +export function isExecuteActionContext(thing: unknown): thing is IChatExecuteActionContext { return typeof thing === 'object' && thing !== null && 'widget' in thing; } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index f4d874d252d..8bcc0fd7632 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event'; import { firstOrDefault } from 'vs/base/common/arrays'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; @@ -16,69 +16,98 @@ import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; -import { IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; +import { MENU_INLINE_CHAT_WIDGET } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; +import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; -const CONTEXT_CHAT_VOICE_INPUT_GETTING_READY = new RawContextKey('chatVoiceInputGettingReady', false, { type: 'boolean', description: localize('chatVoiceInputGettingReady', "True when there is voice input for chat getting ready.") }); -const CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS = new RawContextKey('chatVoiceInputInProgress', false, { type: 'boolean', description: localize('chatVoiceInputInProgress', "True when there is voice input for chat in progress.") }); +const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when there is voice input for chat getting ready.") }); +const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when there is voice input for chat in progress.") }); -interface IChatVoiceInputActionContext { - readonly widget: IChatWidget; - readonly inputValue?: string; +interface IVoiceChatSessionController { + + readonly onDidAcceptInput: Event; + + focusInput(): void; + acceptInput(): void; + updateInput(text: string): void; } -function isVoiceInputActionContext(thing: unknown): thing is IChatVoiceInputActionContext { - return typeof thing === 'object' && thing !== null && 'widget' in thing; -} - -class ChatVoiceInputSession { - - private static instance: ChatVoiceInputSession | undefined = undefined; - static getInstance(instantiationService: IInstantiationService): ChatVoiceInputSession { - if (!ChatVoiceInputSession.instance) { - ChatVoiceInputSession.instance = instantiationService.createInstance(ChatVoiceInputSession); - } - - return ChatVoiceInputSession.instance; +function getController(controller: InlineChatController): IVoiceChatSessionController; +function getController(context: unknown): IVoiceChatSessionController | undefined; +function getController(context: unknown): IVoiceChatSessionController | undefined { + if (context instanceof InlineChatController) { + return { + onDidAcceptInput: context.onDidAcceptInput, + focusInput: () => context.focus(), + acceptInput: () => context.acceptInput(), + updateInput: text => context.updateInput(text) + }; } - private chatVoiceInputInProgressKey = CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.bindTo(this.contextKeyService); - private chatVoiceInputGettingReadyKey = CONTEXT_CHAT_VOICE_INPUT_GETTING_READY.bindTo(this.contextKeyService); + if (isExecuteActionContext(context)) { + return context.widget; + } - private currentChatVoiceInputSession: DisposableStore | undefined = undefined; + return undefined; +} + +class VoiceChatSession { + + private static instance: VoiceChatSession | undefined = undefined; + static getInstance(instantiationService: IInstantiationService): VoiceChatSession { + if (!VoiceChatSession.instance) { + VoiceChatSession.instance = instantiationService.createInstance(VoiceChatSession); + } + + return VoiceChatSession.instance; + } + + private voiceChatInProgressKey = CONTEXT_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); + private voiceChatGettingReadyKey = CONTEXT_VOICE_CHAT_GETTING_READY.bindTo(this.contextKeyService); + + private currentVoiceChatSession: DisposableStore | undefined = undefined; + private voiceChatSessionIds = 0; constructor( @IContextKeyService private readonly contextKeyService: IContextKeyService, @IWorkbenchVoiceRecognitionService private readonly voiceRecognitionService: IWorkbenchVoiceRecognitionService ) { } - async start(context: IChatVoiceInputActionContext, disposables?: IDisposable[]): Promise { + async start(context: IVoiceChatSessionController): Promise { this.stop(); - this.chatVoiceInputGettingReadyKey.set(true); - this.currentChatVoiceInputSession = new DisposableStore(); - for (const disposable of disposables ?? []) { - this.currentChatVoiceInputSession.add(disposable); - } + this.voiceChatGettingReadyKey.set(true); + this.currentVoiceChatSession = new DisposableStore(); const cts = new CancellationTokenSource(); - this.currentChatVoiceInputSession.add(toDisposable(() => cts.dispose(true))); + this.currentVoiceChatSession.add(toDisposable(() => cts.dispose(true))); - context.widget.focusInput(); + context.focusInput(); const onDidTranscribe = await this.voiceRecognitionService.transcribe(cts.token); if (cts.token.isCancellationRequested) { - return; + return Disposable.None; } - this.chatVoiceInputGettingReadyKey.set(false); - this.chatVoiceInputInProgressKey.set(true); + const voiceChatSessionId = this.voiceChatSessionIds++; + + this.voiceChatGettingReadyKey.set(false); + this.voiceChatInProgressKey.set(true); let lastText: string | undefined = undefined; let lastTextSimilarCount = 0; - this.currentChatVoiceInputSession.add(onDidTranscribe(text => { + this.currentVoiceChatSession.add(onDidTranscribe(text => { + if (!text && lastText) { + text = lastText; + } + if (text) { if (lastText && this.isSimilarTranscription(text, lastText)) { lastTextSimilarCount++; @@ -88,17 +117,23 @@ class ChatVoiceInputSession { } if (lastTextSimilarCount >= 2) { - context.widget.acceptInput(); + context.acceptInput(); } else { - context.widget.updateInput(text); + context.updateInput(text); } } })); - this.currentChatVoiceInputSession.add(context.widget.onDidAcceptInput(() => { + this.currentVoiceChatSession.add(context.onDidAcceptInput(() => { this.stop(); })); + + return toDisposable(() => { + if (this.voiceChatSessionIds === voiceChatSessionId) { + this.stop(); + } + }); } private isSimilarTranscription(textA: string, textB: string): boolean { @@ -116,131 +151,235 @@ class ChatVoiceInputSession { } stop(): void { - if (!this.currentChatVoiceInputSession) { + if (!this.currentVoiceChatSession) { return; } - this.currentChatVoiceInputSession.dispose(); - this.currentChatVoiceInputSession = undefined; + this.currentVoiceChatSession.dispose(); + this.currentVoiceChatSession = undefined; - this.chatVoiceInputGettingReadyKey.set(false); - this.chatVoiceInputInProgressKey.set(false); + this.voiceChatGettingReadyKey.set(false); + this.voiceChatInProgressKey.set(false); } } -class StartChatVoiceInputAction extends Action2 { +class VoiceChatInChatViewAction extends Action2 { - static readonly ID = 'workbench.action.chat.startVoiceInput'; + static readonly ID = 'workbench.action.chat.voiceChatInChatView'; constructor() { super({ - id: StartChatVoiceInputAction.ID, + id: VoiceChatInChatViewAction.ID, title: { - value: localize('interactive.voiceInput.label', "Start Voice Input"), - original: 'Start Voice Input' - }, - category: CHAT_CATEGORY, - f1: true, - icon: Codicon.record, - precondition: CONTEXT_CHAT_VOICE_INPUT_GETTING_READY.negate(), - menu: { - id: MenuId.ChatExecute, - when: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS.negate(), - group: 'navigation', - order: -1 - } - }); - } - - async run(accessor: ServicesAccessor, ...args: any[]): Promise { - const chatWidgetService = accessor.get(IChatWidgetService); - const chatService = accessor.get(IChatService); - const instantiationService = accessor.get(IInstantiationService); - - let context = args[0]; - if (!isVoiceInputActionContext(context)) { - if (chatWidgetService.lastFocusedWidget?.hasInputFocus()) { - context = { widget: chatWidgetService.lastFocusedWidget }; - } else { - const provider = firstOrDefault(chatService.getProviderInfos()); - if (provider) { - context = { widget: await chatWidgetService.revealViewForProvider(provider.id) }; - } - } - } - - if (!isVoiceInputActionContext(context)) { - return; - } - - ChatVoiceInputSession.getInstance(instantiationService).start(context); - } -} - -class StopChatVoiceInputAction extends Action2 { - - static readonly ID = 'workbench.action.chat.stopVoiceInput'; - - constructor() { - super({ - id: StopChatVoiceInputAction.ID, - title: { - value: localize('interactive.stopVoiceInput.label', "Stop Voice Input"), - original: 'Stop Voice Input' - }, - category: CHAT_CATEGORY, - f1: true, - precondition: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS, - icon: spinningLoading, - menu: { - id: MenuId.ChatExecute, - when: CONTEXT_CHAT_VOICE_INPUT_IN_PROGRESS, - group: 'navigation', - order: -1 - } - }); - } - - run(accessor: ServicesAccessor): void { - ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).stop(); - } -} - -class VoiceQuickChatAction extends Action2 { - - static readonly ID = 'workbench.action.chat.voiceQuickChat'; - - constructor() { - super({ - id: VoiceQuickChatAction.ID, - title: { - value: localize('interactive.voiceQuickChat.label', "Quick Chat with Voice Input"), - original: 'Quick Chat with Voice Input' + value: localize('workbench.action.chat.voiceChatInView.label', "Voice Chat in Chat View"), + original: 'Voice Chat in Chat View' }, category: CHAT_CATEGORY, + precondition: CONTEXT_PROVIDER_EXISTS, f1: true }); } - run(accessor: ServicesAccessor): void { + async run(accessor: ServicesAccessor): Promise { + const chatWidgetService = accessor.get(IChatWidgetService); + const chatService = accessor.get(IChatService); + const instantiationService = accessor.get(IInstantiationService); + + const provider = firstOrDefault(chatService.getProviderInfos()); + if (provider) { + const controller = await chatWidgetService.revealViewForProvider(provider.id); + if (controller) { + VoiceChatSession.getInstance(instantiationService).start(controller); + } + } + } +} + +class InlineVoiceChatAction extends Action2 { + + static readonly ID = 'workbench.action.chat.inlineVoiceChat'; + + constructor() { + super({ + id: InlineVoiceChatAction.ID, + title: { + value: localize('workbench.action.chat.inlineVoiceChat', "Inline Voice Chat"), + original: 'Inline Voice Chat' + }, + category: CHAT_CATEGORY, + precondition: CONTEXT_PROVIDER_EXISTS, + f1: true + }); + } + + async run(accessor: ServicesAccessor): Promise { + const editorService = accessor.get(IEditorService); + const instantiationService = accessor.get(IInstantiationService); + + const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); + if (!activeCodeEditor) { + return; + } + + const controller = InlineChatController.get(activeCodeEditor); + if (!controller) { + return; + } + + const inlineChatSession = controller.run(); + + const disposable = await VoiceChatSession.getInstance(instantiationService).start(getController(controller)); + + inlineChatSession.finally(() => disposable.dispose()); + } +} + +class QuickVoiceChatAction extends Action2 { + + static readonly ID = 'workbench.action.chat.quickVoiceChat'; + + constructor() { + super({ + id: QuickVoiceChatAction.ID, + title: { + value: localize('workbench.action.chat.quickVoiceChat.label', "Quick Voice Chat"), + original: 'Quick Voice Chat' + }, + category: CHAT_CATEGORY, + precondition: CONTEXT_PROVIDER_EXISTS, + f1: true + }); + } + + async run(accessor: ServicesAccessor): Promise { const quickChatService = accessor.get(IQuickChatService); const chatWidgetService = accessor.get(IChatWidgetService); const instantiationService = accessor.get(IInstantiationService); quickChatService.open(); - const disposables: IDisposable[] = []; - Event.once(quickChatService.onDidClose)(() => ChatVoiceInputSession.getInstance(instantiationService).stop(), undefined, disposables); - - const widget = chatWidgetService.lastFocusedWidget; - if (widget) { - ChatVoiceInputSession.getInstance(accessor.get(IInstantiationService)).start({ widget }, disposables); + const controller = chatWidgetService.lastFocusedWidget; + if (controller) { + const disposable = await VoiceChatSession.getInstance(instantiationService).start(controller); + Event.once(quickChatService.onDidClose)(() => disposable.dispose()); } } } -export function registerChatVoiceInputActions() { - registerAction2(StartChatVoiceInputAction); - registerAction2(StopChatVoiceInputAction); - registerAction2(VoiceQuickChatAction); +class StartVoiceChatAction extends Action2 { + + static readonly ID = 'workbench.action.chat.startVoiceChat'; + + constructor() { + super({ + id: StartVoiceChatAction.ID, + title: { + value: localize('workbench.action.chat.startVoiceChat', "Start Voice Chat"), + original: 'Start Voice Chat' + }, + icon: Codicon.record, + precondition: CONTEXT_VOICE_CHAT_GETTING_READY.negate(), + menu: [{ + id: MenuId.ChatExecute, + when: CONTEXT_VOICE_CHAT_IN_PROGRESS.negate(), + group: 'navigation', + order: -1 + }, { + id: MENU_INLINE_CHAT_WIDGET, + when: CONTEXT_VOICE_CHAT_IN_PROGRESS.negate(), + group: 'main', + order: -1 + }] + }); + } + + async run(accessor: ServicesAccessor, context: unknown): Promise { + const editorService = accessor.get(IEditorService); + const chatWidgetService = accessor.get(IChatWidgetService); + const chatService = accessor.get(IChatService); + const instantiationService = accessor.get(IInstantiationService); + + let controller = getController(context); + if (!controller) { + + // Without a controller, this action potentially executed from + // a global keybinding, and thus we have to find the chat + // input that is currently focussed, or fallback to opening one + + // 1.) a chat input widget has focus + { + if (chatWidgetService.lastFocusedWidget?.hasInputFocus()) { + controller = chatWidgetService.lastFocusedWidget; + } + } + + // 2.) a inline chat input widget has focus + { + const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); + if (activeCodeEditor) { + const chatInput = InlineChatController.get(activeCodeEditor); + if (chatInput?.hasFocus()) { + controller = getController(chatInput); + } + } + } + + // 3.) open a chat view + { + const provider = firstOrDefault(chatService.getProviderInfos()); + if (provider) { + controller = await chatWidgetService.revealViewForProvider(provider.id); + } + } + } + + if (!controller) { + return; + } + + VoiceChatSession.getInstance(instantiationService).start(controller); + } +} + +class StopVoiceChatAction extends Action2 { + + static readonly ID = 'workbench.action.chat.stopVoiceChat'; + + constructor() { + super({ + id: StopVoiceChatAction.ID, + title: { + value: localize('workbench.action.chat.stopVoiceChat.label', "Stop Voice Chat"), + original: 'Stop Voice Chat' + }, + category: CHAT_CATEGORY, + f1: true, + precondition: CONTEXT_VOICE_CHAT_IN_PROGRESS, + icon: spinningLoading, + menu: [{ + id: MenuId.ChatExecute, + when: CONTEXT_VOICE_CHAT_IN_PROGRESS, + group: 'navigation', + order: -1 + }, { + id: MENU_INLINE_CHAT_WIDGET, + when: CONTEXT_VOICE_CHAT_IN_PROGRESS, + group: 'main', + order: -1 + }] + }); + } + + run(accessor: ServicesAccessor): void { + VoiceChatSession.getInstance(accessor.get(IInstantiationService)).stop(); + } +} + +export function registerChatVoiceInputActions() { + registerAction2(VoiceChatInChatViewAction); + registerAction2(QuickVoiceChatAction); + registerAction2(InlineVoiceChatAction); + + registerAction2(StartVoiceChatAction); + registerAction2(StopVoiceChatAction); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index f91de7e3ee8..023fee9cfd2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -100,6 +100,8 @@ export class InlineChatController implements IEditorContribution { private _messages = this._store.add(new Emitter()); + readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); + private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); private readonly _stashedSession: MutableDisposable = this._store.add(new MutableDisposable()); private _activeSession?: Session; @@ -395,8 +397,7 @@ export class InlineChatController implements IEditorContribution { this._zone.value.widget.placeholder = this._getPlaceholderText(); if (options.message) { - this._zone.value.widget.value = options.message; - this._zone.value.widget.selectAll(); + this.updateInput(options.message); aria.alert(options.message); delete options.message; } @@ -758,6 +759,11 @@ export class InlineChatController implements IEditorContribution { this._messages.fire(Message.ACCEPT_INPUT); } + updateInput(text: string): void { + this._zone.value.widget.value = text; + this._zone.value.widget.selectAll(); + } + regenerate(): void { this._messages.fire(Message.RERUN_INPUT); } @@ -780,6 +786,10 @@ export class InlineChatController implements IEditorContribution { this._zone.value.widget.focus(); } + hasFocus(): boolean { + return this._zone.value.widget.hasFocus(); + } + populateHistory(up: boolean) { const len = InlineChatController._promptHistory.length; if (len === 0) { diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 3a10c4afa7b..5688961771f 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -64,6 +64,7 @@ class VoiceTranscriptionWorkletNode extends AudioWorkletNode { // TODO@voice // - add native module test to ensure module loads +// - allow to cancel voice recording from window progress export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { declare readonly _serviceBrand: undefined; From 08b1b5bea836e4e403b9927a3c54593f22790c3e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Aug 2023 12:55:48 +0200 Subject: [PATCH 134/607] voice - allow to cancel from global progress --- .../actions/chatVoiceInputActions.ts | 4 +- .../workbenchVoiceRecognitionService.ts | 46 +++++++++++++------ 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 8bcc0fd7632..01d8c65cff1 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -90,7 +90,9 @@ class VoiceChatSession { context.focusInput(); - const onDidTranscribe = await this.voiceRecognitionService.transcribe(cts.token); + const onDidTranscribe = await this.voiceRecognitionService.transcribe(cts.token, { + onDidCancel: () => this.stop() + }); if (cts.token.isCancellationRequested) { return Disposable.None; } diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index 5688961771f..c1863701239 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { CancellationToken } from 'vs/base/common/cancellation'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -16,17 +16,26 @@ import { INotificationService } from 'vs/platform/notification/common/notificati export const IWorkbenchVoiceRecognitionService = createDecorator('workbenchVoiceRecognitionService'); +export interface IWorkbenchVoiceRecognitionOptions { + + /** + * Optional event that is fired when the user cancels the voice recognition. + */ + readonly onDidCancel?: () => void; +} + export interface IWorkbenchVoiceRecognitionService { readonly _serviceBrand: undefined; /** - * Starts listening to the microphone transcribing the voice to text. + * Starts listening to the microphone transcribing the voice to text. Microphone + * recording starts when the returned promise is resolved. * * @param cancellation a cancellation token to stop transcribing and * listening to the microphone. */ - transcribe(cancellation: CancellationToken): Promise>; + transcribe(cancellation: CancellationToken, options?: IWorkbenchVoiceRecognitionOptions): Promise>; } class VoiceTranscriptionWorkletNode extends AudioWorkletNode { @@ -64,7 +73,6 @@ class VoiceTranscriptionWorkletNode extends AudioWorkletNode { // TODO@voice // - add native module test to ensure module loads -// - allow to cancel voice recording from window progress export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { declare readonly _serviceBrand: undefined; @@ -79,22 +87,28 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit @INotificationService private readonly notificationService: INotificationService ) { } - async transcribe(cancellation: CancellationToken): Promise> { - const onDidTranscribe = new Emitter(); - cancellation.onCancellationRequested(() => onDidTranscribe.dispose()); + async transcribe(cancellation: CancellationToken, options?: IWorkbenchVoiceRecognitionOptions): Promise> { + const cts = new CancellationTokenSource(cancellation); - await this.doTranscribe(onDidTranscribe, cancellation); + const onDidTranscribe = new Emitter(); + cts.token.onCancellationRequested(() => { + onDidTranscribe.dispose(); + options?.onDidCancel?.(); + }); + + await this.doTranscribe(onDidTranscribe, cts); return onDidTranscribe.event; } - private doTranscribe(onDidTranscribe: Emitter, token: CancellationToken): Promise { + private doTranscribe(onDidTranscribe: Emitter, cts: CancellationTokenSource): Promise { const recordingReady = new DeferredPromise(); - token.onCancellationRequested(() => recordingReady.complete()); + cts.token.onCancellationRequested(() => recordingReady.complete()); this.progressService.withProgress({ location: ProgressLocation.Window, title: localize('voiceTranscription', "Voice Transcription"), + cancellable: true }, async progress => { const recordingDone = new DeferredPromise(); try { @@ -110,7 +124,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit } }); - if (token.isCancellationRequested) { + if (cts.token.isCancellationRequested) { return; } @@ -121,7 +135,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit const microphoneSource = audioContext.createMediaStreamSource(microphoneDevice); - token.onCancellationRequested(() => { + cts.token.onCancellationRequested(() => { try { for (const track of microphoneDevice.getTracks()) { track.stop(); @@ -136,7 +150,7 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit await audioContext.audioWorklet.addModule(FileAccess.asBrowserUri('vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.js').toString(true)); - if (token.isCancellationRequested) { + if (cts.token.isCancellationRequested) { return; } @@ -144,9 +158,9 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, channelCountMode: 'explicit' }, onDidTranscribe, this.sharedProcessService); - await voiceTranscriptionTarget.start(token); + await voiceTranscriptionTarget.start(cts.token); - if (token.isCancellationRequested) { + if (cts.token.isCancellationRequested) { return; } @@ -162,6 +176,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit recordingReady.error(error); recordingDone.error(error); } + }, () => { + cts.cancel(); }); return recordingReady.p; From 518f1dcff7b5167104a5c57b0cb78978b5576e83 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Aug 2023 13:04:10 +0200 Subject: [PATCH 135/607] voice - prefer quick chat as fallback --- .../actions/chatVoiceInputActions.ts | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 01d8c65cff1..91e0e6490c5 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -25,6 +25,7 @@ import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/in import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when there is voice input for chat getting ready.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when there is voice input for chat in progress.") }); @@ -298,25 +299,23 @@ class StartVoiceChatAction extends Action2 { async run(accessor: ServicesAccessor, context: unknown): Promise { const editorService = accessor.get(IEditorService); const chatWidgetService = accessor.get(IChatWidgetService); - const chatService = accessor.get(IChatService); const instantiationService = accessor.get(IInstantiationService); + const commandService = accessor.get(ICommandService); let controller = getController(context); if (!controller) { // Without a controller, this action potentially executed from // a global keybinding, and thus we have to find the chat - // input that is currently focussed, or fallback to opening one + // input that is currently focussed, or have a fallback // 1.) a chat input widget has focus - { - if (chatWidgetService.lastFocusedWidget?.hasInputFocus()) { - controller = chatWidgetService.lastFocusedWidget; - } + if (chatWidgetService.lastFocusedWidget?.hasInputFocus()) { + controller = chatWidgetService.lastFocusedWidget; } // 2.) a inline chat input widget has focus - { + if (!controller) { const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); if (activeCodeEditor) { const chatInput = InlineChatController.get(activeCodeEditor); @@ -326,12 +325,9 @@ class StartVoiceChatAction extends Action2 { } } - // 3.) open a chat view - { - const provider = firstOrDefault(chatService.getProviderInfos()); - if (provider) { - controller = await chatWidgetService.revealViewForProvider(provider.id); - } + // 3.) open a quick chat view + if (!controller) { + return commandService.executeCommand(QuickVoiceChatAction.ID); } } From 92b1178b552431333feba64a270a514055e96ccc Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 12:37:34 +0200 Subject: [PATCH 136/607] Fixes #190325 --- .../diffEditorWidget2/accessibleDiffViewer.ts | 5 +-- src/vs/editor/common/core/offsetRange.ts | 34 +++++++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts index d66333e1aec..32f45d85186 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts @@ -18,6 +18,7 @@ import { applyStyle } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { DiffReview } from 'vs/editor/browser/widget/diffReview'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { LineRangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; @@ -163,7 +164,7 @@ class ViewModel extends Disposable { const groups = this.groups.get(); if (!groups || groups.length <= 1) { return; } subtransaction(tx, tx => { - this._currentGroupIdx.set((this._currentGroupIdx.get() + groups.length + delta) % groups.length, tx); + this._currentGroupIdx.set(OffsetRange.ofLength(groups.length).clipCyclic(this._currentGroupIdx.get() + delta), tx); this._currentElementIdx.set(0, tx); }); } @@ -175,7 +176,7 @@ class ViewModel extends Disposable { const group = this.currentGroup.get(); if (!group || group.lines.length <= 1) { return; } transaction(tx => { - this._currentElementIdx.set((this._currentElementIdx.get() + group.lines.length + delta) % group.lines.length, tx); + this._currentElementIdx.set(OffsetRange.ofLength(group.lines.length).clip(this._currentElementIdx.get() + delta), tx); }); } diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index d4129b1c532..27e60bca2df 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -34,6 +34,10 @@ export class OffsetRange { return new OffsetRange(start, endExclusive); } + public static ofLength(length: number): OffsetRange { + return new OffsetRange(0, length); + } + constructor(public readonly start: number, public readonly endExclusive: number) { if (start > endExclusive) { throw new BugIndicatingError(`Invalid range: ${this.toString()}`); @@ -102,6 +106,36 @@ export class OffsetRange { public slice(arr: T[]): T[] { return arr.slice(this.start, this.endExclusive); } + + /** + * Returns the given value if it is contained in this instance, otherwise the closest value that is contained. + * The range must not be empty. + */ + public clip(value: number): number { + if (this.isEmpty) { + throw new BugIndicatingError(`Invalid clipping range: ${this.toString()}`); + } + return Math.max(this.start, Math.min(this.endExclusive - 1, value)); + } + + /** + * Returns `r := value + k * length` such that `r` is contained in this range. + * The range must not be empty. + * + * E.g. `[5, 10).clipCyclic(10) === 5`, `[5, 10).clipCyclic(11) === 6` and `[5, 10).clipCyclic(4) === 9`. + */ + public clipCyclic(value: number): number { + if (this.isEmpty) { + throw new BugIndicatingError(`Invalid clipping range: ${this.toString()}`); + } + if (value < this.start) { + return this.endExclusive - ((this.start - value) % this.length); + } + if (value >= this.endExclusive) { + return this.start + ((value - this.start) % this.length); + } + return value; + } } export class OffsetRangeSet { From 7b7f33106a88a315be47f4db1cd5358833f1a371 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Aug 2023 14:16:31 +0200 Subject: [PATCH 137/607] fix #189872 (#191206) --- .../contrib/extensions/browser/fileBasedRecommendations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index b3417372e48..b886e417338 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -281,7 +281,7 @@ export class FileBasedRecommendations extends ExtensionRecommendations { const language = model.getLanguageId(); const languageName = this.languageService.getLanguageName(language); if (importantRecommendations.size && - this.promptRecommendedExtensionForFileType(languageName && isImportantRecommendationForLanguage && language !== PLAINTEXT_LANGUAGE_ID ? localize('languageName', "{0} language", languageName) : basename(uri), language, [...importantRecommendations])) { + this.promptRecommendedExtensionForFileType(languageName && isImportantRecommendationForLanguage && language !== PLAINTEXT_LANGUAGE_ID ? localize('languageName', "the {0} language", languageName) : basename(uri), language, [...importantRecommendations])) { return; } } From 1e8ccdc0ff61b62177be63644c5526252b961e9b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Aug 2023 14:25:39 +0200 Subject: [PATCH 138/607] #189481 scope to web (#191204) --- .../browser/userDataSyncWorkbenchService.ts | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 0668f9e3b6a..d509a21c76c 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -171,17 +171,19 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private async initialize(): Promise { - const authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); - if (this.currentSessionId === undefined && authenticationSession?.id) { - if (this.environmentService.options?.settingsSyncOptions?.authenticationProvider && this.environmentService.options.settingsSyncOptions.enabled) { - this.currentSessionId = authenticationSession.id; - } + if (isWeb) { + const authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); + if (this.currentSessionId === undefined && authenticationSession?.id) { + if (this.environmentService.options?.settingsSyncOptions?.authenticationProvider && this.environmentService.options.settingsSyncOptions.enabled) { + this.currentSessionId = authenticationSession.id; + } - // Backward compatibility - else if (this.useWorkbenchSessionId) { - this.currentSessionId = authenticationSession.id; + // Backward compatibility + else if (this.useWorkbenchSessionId) { + this.currentSessionId = authenticationSession.id; + } + this.useWorkbenchSessionId = false; } - this.useWorkbenchSessionId = false; } await this.update(); From cafcb59c16b5fcb2ae2db76cea0ba2596df8b7db Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Aug 2023 14:32:29 +0200 Subject: [PATCH 139/607] fix #190228 (#191207) --- .../workbench/contrib/extensions/browser/extensionEditor.ts | 6 ++---- .../contrib/extensions/browser/media/extensionEditor.css | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 0eb852277ab..cbd45d0ebeb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -855,12 +855,10 @@ export class ExtensionEditor extends EditorPane { extensionPackReadme.style.maxWidth = '882px'; const extensionPack = append(extensionPackReadme, $('div', { class: 'extension-pack' })); - if (manifest.extensionPack!.length <= 3) { + if (manifest.extensionPack!.length < 3) { extensionPackReadme.classList.add('one-row'); - } else if (manifest.extensionPack!.length <= 6) { + } else if (manifest.extensionPack!.length < 5) { extensionPackReadme.classList.add('two-rows'); - } else if (manifest.extensionPack!.length <= 9) { - extensionPackReadme.classList.add('three-rows'); } else { extensionPackReadme.classList.add('more-rows'); } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 174b332538b..575e6870b54 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -517,10 +517,6 @@ height: 224px; } -.extension-editor > .body > .content > .details > .readme-container > .extension-pack-readme.three-rows > .extension-pack { - height: 306px; -} - .extension-editor > .body > .content > .details > .readme-container > .extension-pack-readme.more-rows > .extension-pack { height: 326px; } From 0050133a605178b015565e3b02c2e418d2fe8e3b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Aug 2023 14:47:28 +0200 Subject: [PATCH 140/607] fix #189574 (#191209) --- .../common/abstractExtensionManagementService.ts | 2 +- .../contrib/extensions/browser/extensions.contribution.ts | 2 +- .../contrib/extensions/browser/extensionsWorkbenchService.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 829a9b679c9..bcbc16146d7 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -154,7 +154,7 @@ export abstract class AbstractExtensionManagementService extends Disposable impl } async toggleAppliationScope(extension: ILocalExtension, fromProfileLocation: URI): Promise { - if (isApplicationScopedExtension(extension.manifest)) { + if (isApplicationScopedExtension(extension.manifest) || extension.isBuiltin) { return extension; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 78f7de871fa..3d40bfa801d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -1421,7 +1421,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '2_configure', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('isDefaultApplicationScopedExtension').negate()), + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.has('isDefaultApplicationScopedExtension').negate(), ContextKeyExpr.has('isBuiltinExtension').negate()), order: 3 }, run: async (accessor: ServicesAccessor, id: string) => { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 96659a5f228..154696503e3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1622,7 +1622,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } async toggleApplyExtensionToAllProfiles(extension: IExtension): Promise { - if (!extension.local || isApplicationScopedExtension(extension.local.manifest)) { + if (!extension.local || isApplicationScopedExtension(extension.local.manifest) || extension.isBuiltin) { return; } await this.extensionManagementService.toggleAppliationScope(extension.local, this.userDataProfileService.currentProfile.extensionsResource); From d0b353aa4d1d6c7ec62039628c22919b4f8ccfe9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 06:00:42 -0700 Subject: [PATCH 141/607] Add default values to EnvironmentVariableMutatorOptions --- src/vscode-dts/vscode.d.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 9904b311e50..d440e2d2fc1 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11339,13 +11339,14 @@ declare module 'vscode' { */ export interface EnvironmentVariableMutatorOptions { /** - * Apply to the environment just before the process is created. + * Apply to the environment just before the process is created. Defaults to false. */ applyAtProcessCreation?: boolean; /** * Apply to the environment in the shell integration script. Note that this _will not_ apply - * the mutator if shell integration is disabled or not working for some reason. + * the mutator if shell integration is disabled or not working for some reason. Defaults to + * false. */ applyAtShellIntegration?: boolean; } From 1e822ef3f504b3940b26b1a1de1941ea06dafe48 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 24 Aug 2023 15:00:47 +0200 Subject: [PATCH 142/607] fix #190363 (#191210) --- .../browser/userDataProfileImportExportService.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts index cea4e9de5f8..9dc107dd7fb 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts @@ -324,14 +324,15 @@ export class UserDataProfileImportExportService extends Disposable implements IU let result: { name: string; items: ReadonlyArray } | undefined; disposables.add(Event.any(quickPick.onDidCustom, quickPick.onDidAccept)(() => { - if (!quickPick.value) { - quickPick.validationMessage = localize('name required', "Provide a name for the new profile"); + const name = quickPick.value.trim(); + if (!name) { + quickPick.validationMessage = localize('name required', "Profile name is required and must be a non-empty value."); quickPick.severity = Severity.Error; } if (quickPick.validationMessage) { return; } - result = { name: quickPick.value, items: quickPick.selectedItems }; + result = { name, items: quickPick.selectedItems }; quickPick.hide(); quickPick.severity = Severity.Ignore; quickPick.validationMessage = undefined; From e64b9487bfa0aa84ae43aad57a0555a3cf20ef8f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 13:00:54 +0200 Subject: [PATCH 143/607] Fixes #189039 --- .../browser/widget/diffEditorWidget2/diffEditorEditors.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index ee764c81aed..877f79ce430 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -50,6 +50,8 @@ export class DiffEditorEditors extends Disposable { /** @description update editor options */ _options.editorOptions.read(reader); + this._options.renderSideBySide.read(reader); + this.modified.updateOptions(this._adjustOptionsForRightHandSide(reader, changeSummary)); this.original.updateOptions(this._adjustOptionsForLeftHandSide(reader, changeSummary)); })); @@ -93,7 +95,11 @@ export class DiffEditorEditors extends Disposable { result.wordWrapOverride1 = 'off'; result.wordWrapOverride2 = 'off'; result.stickyScroll = { enabled: false }; + + // Disable unicode highlighting for the original side in inline mode, as they are not shown anyway. + result.unicodeHighlight = { nonBasicASCII: false, ambiguousCharacters: false, invisibleCharacters: false }; } else { + result.unicodeHighlight = this._options.editorOptions.get().unicodeHighlight; result.wordWrapOverride1 = this._options.diffWordWrap.get(); } if (changedOptions.originalAriaLabel) { From 1c3865f9ff21ce1aa46bee93f92d3e413d2e7b6d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Aug 2023 15:23:02 +0200 Subject: [PATCH 144/607] voice - make actions conditional --- .../node/voiceRecognitionService.ts | 8 +++++--- .../actions/chatVoiceInputActions.ts | 14 +++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index d2279001d75..fdcd3ac332d 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -6,6 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ILogService } from 'vs/platform/log/common/log'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IProductService } from 'vs/platform/product/common/productService'; export const IVoiceRecognitionService = createDecorator('voiceRecognitionService'); @@ -31,14 +32,15 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { declare readonly _serviceBrand: undefined; constructor( - @ILogService private readonly logService: ILogService + @ILogService private readonly logService: ILogService, + @IProductService private readonly productService: IProductService ) { } async transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise { this.logService.info(`[voice] transcribe(${channelData.length}): Begin`); - const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; - if (!modulePath) { + const modulePath = process.env.VSCODE_VOICE_MODULE_PATH; // TODO@bpasero package + if (!modulePath || this.productService.quality === 'stable') { this.logService.error(`[voice] transcribe(${channelData.length}): Voice recognition not yet supported`); throw new Error('Voice recognition not yet supported!'); } diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts index 91e0e6490c5..9ea3b0cde45 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts @@ -26,6 +26,8 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import product from 'vs/platform/product/common/product'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when there is voice input for chat getting ready.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when there is voice input for chat in progress.") }); @@ -374,10 +376,12 @@ class StopVoiceChatAction extends Action2 { } export function registerChatVoiceInputActions() { - registerAction2(VoiceChatInChatViewAction); - registerAction2(QuickVoiceChatAction); - registerAction2(InlineVoiceChatAction); + if (typeof process.env.VSCODE_VOICE_MODULE_PATH === 'string' && product.quality !== 'stable') { // TODO@bpasero package + registerAction2(VoiceChatInChatViewAction); + registerAction2(QuickVoiceChatAction); + registerAction2(InlineVoiceChatAction); - registerAction2(StartVoiceChatAction); - registerAction2(StopVoiceChatAction); + registerAction2(StartVoiceChatAction); + registerAction2(StopVoiceChatAction); + } } From 06f6caff51dafa6e9a855e4e747085047ecb8f4b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 11:03:02 +0200 Subject: [PATCH 145/607] Renames smart to legacy, standard to advanced --- src/vs/editor/browser/editorBrowser.ts | 2 +- src/vs/editor/browser/services/editorWorkerService.ts | 2 +- src/vs/editor/browser/widget/diffEditorWidget.ts | 2 +- .../widget/diffEditorWidget2/diffEditorViewModel.ts | 4 ++-- .../browser/widget/diffEditorWidget2/diffEditorWidget2.ts | 2 +- src/vs/editor/browser/widget/diffNavigator.ts | 2 +- src/vs/editor/browser/widget/diffReview.ts | 2 +- ...dLinesDiffComputer.ts => advancedLinesDiffComputer.ts} | 2 +- src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts | 2 +- ...artLinesDiffComputer.ts => legacyLinesDiffComputer.ts} | 2 +- src/vs/editor/common/diff/linesDiffComputers.ts | 8 ++++---- src/vs/editor/common/services/editorSimpleWorker.ts | 2 +- src/vs/editor/common/services/editorWorker.ts | 2 +- src/vs/editor/test/common/diff/diffComputer.test.ts | 2 +- .../test/common/services/testEditorWorkerService.ts | 2 +- src/vs/editor/test/node/diffing/diffingFixture.test.ts | 6 +++--- src/vs/editor/test/node/diffing/lineRangeMapping.test.ts | 2 +- src/vs/workbench/api/browser/mainThreadEditors.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- .../workbench/contrib/notebook/common/notebookCommon.ts | 2 +- .../workbench/contrib/scm/browser/dirtydiffDecorator.ts | 2 +- 21 files changed, 27 insertions(+), 27 deletions(-) rename src/vs/editor/common/diff/{standardLinesDiffComputer.ts => advancedLinesDiffComputer.ts} (99%) rename src/vs/editor/common/diff/{smartLinesDiffComputer.ts => legacyLinesDiffComputer.ts} (99%) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index ae6cfd542d3..1ea2dff1bb6 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -15,7 +15,7 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { IWordAtPosition } from 'vs/editor/common/core/wordHelper'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/cursorEvents'; -import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { GlyphMarginLane, ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, PositionAffinity } from 'vs/editor/common/model'; import { InjectedText } from 'vs/editor/common/modelLineProjectionData'; diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index 6757066169f..993fe8e813c 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -24,7 +24,7 @@ import { canceled, onUnexpectedError } from 'vs/base/common/errors'; import { UnicodeHighlighterOptions } from 'vs/editor/common/services/unicodeTextModelHighlighter'; import { IEditorWorkerHost } from 'vs/editor/common/services/editorWorkerHost'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; import { ILinesDiffComputerOptions, LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; import { LineRange } from 'vs/editor/common/core/lineRange'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 4eae29a335e..e1a43acbaf6 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -40,7 +40,7 @@ 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 { StringBuilder } from 'vs/editor/common/core/stringBuilder'; -import { IChange, ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IChange, ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 8dea4d6cc56..7927623f49b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -11,7 +11,7 @@ import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange import { Range } from 'vs/editor/common/core/range'; import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; -import { StandardLinesDiffComputer, lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/standardLinesDiffComputer'; +import { AdvancedLinesDiffComputer, lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/advancedLinesDiffComputer'; import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper'; @@ -162,7 +162,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo debouncer.cancel(); contentChangedSignal.read(reader); documentDiffProviderOptionChanged.read(reader); - readHotReloadableExport(StandardLinesDiffComputer, reader); + readHotReloadableExport(AdvancedLinesDiffComputer, reader); this._isDiffUpToDate.set(false, undefined); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 5bc2b098e2b..280c2bb7b9a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -30,7 +30,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; -import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { EditorType, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; diff --git a/src/vs/editor/browser/widget/diffNavigator.ts b/src/vs/editor/browser/widget/diffNavigator.ts index 0f34a1e5960..1edce99edf6 100644 --- a/src/vs/editor/browser/widget/diffNavigator.ts +++ b/src/vs/editor/browser/widget/diffNavigator.ts @@ -10,7 +10,7 @@ import * as objects from 'vs/base/common/objects'; import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { ICursorPositionChangedEvent } from 'vs/editor/common/cursorEvents'; import { Range } from 'vs/editor/common/core/range'; -import { ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index ea813d85aa5..268d41d2f3b 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -19,7 +19,7 @@ import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; -import { ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { ILanguageIdCodec } from 'vs/editor/common/languages'; import { ILanguageService } from 'vs/editor/common/languages/language'; diff --git a/src/vs/editor/common/diff/standardLinesDiffComputer.ts b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts similarity index 99% rename from src/vs/editor/common/diff/standardLinesDiffComputer.ts rename to src/vs/editor/common/diff/advancedLinesDiffComputer.ts index 20279a9fe68..61fd21d9c29 100644 --- a/src/vs/editor/common/diff/standardLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts @@ -17,7 +17,7 @@ import { optimizeSequenceDiffs, removeRandomLineMatches, removeRandomMatches, sm import { MyersDiffAlgorithm } from 'vs/editor/common/diff/algorithms/myersDiffAlgorithm'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LineRangeMapping, LinesDiff, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; -export class StandardLinesDiffComputer implements ILinesDiffComputer { +export class AdvancedLinesDiffComputer implements ILinesDiffComputer { private readonly dynamicProgrammingDiffing = new DynamicProgrammingDiffing(); private readonly myersDiffingAlgorithm = new MyersDiffAlgorithm(); diff --git a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts b/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts index c79d557e409..cef14d466c8 100644 --- a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts +++ b/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts @@ -5,7 +5,7 @@ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { ISequence, SequenceDiff } from 'vs/editor/common/diff/algorithms/diffAlgorithm'; -import { LineSequence, LinesSliceCharSequence } from 'vs/editor/common/diff/standardLinesDiffComputer'; +import { LineSequence, LinesSliceCharSequence } from 'vs/editor/common/diff/advancedLinesDiffComputer'; export function optimizeSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { let result = sequenceDiffs; diff --git a/src/vs/editor/common/diff/smartLinesDiffComputer.ts b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts similarity index 99% rename from src/vs/editor/common/diff/smartLinesDiffComputer.ts rename to src/vs/editor/common/diff/legacyLinesDiffComputer.ts index fe7fdcdc5bf..5ea6524a41e 100644 --- a/src/vs/editor/common/diff/smartLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts @@ -13,7 +13,7 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; const MINIMUM_MATCHING_CHARACTER_LENGTH = 3; -export class SmartLinesDiffComputer implements ILinesDiffComputer { +export class LegacyLinesDiffComputer implements ILinesDiffComputer { computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): LinesDiff { const diffComputer = new DiffComputer(originalLines, modifiedLines, { maxComputationTime: options.maxComputationTimeMs, diff --git a/src/vs/editor/common/diff/linesDiffComputers.ts b/src/vs/editor/common/diff/linesDiffComputers.ts index 415c72e8f04..727b91455b7 100644 --- a/src/vs/editor/common/diff/linesDiffComputers.ts +++ b/src/vs/editor/common/diff/linesDiffComputers.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SmartLinesDiffComputer } from 'vs/editor/common/diff/smartLinesDiffComputer'; -import { StandardLinesDiffComputer } from 'vs/editor/common/diff/standardLinesDiffComputer'; +import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; +import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; export const linesDiffComputers = { - getLegacy: () => new SmartLinesDiffComputer(), - getAdvanced: () => new StandardLinesDiffComputer(), + getLegacy: () => new LegacyLinesDiffComputer(), + getAdvanced: () => new AdvancedLinesDiffComputer(), }; diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 2c26721fd47..ca8364f62d3 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -20,7 +20,7 @@ import { createMonacoBaseAPI } from 'vs/editor/common/services/editorBaseApi'; import { IEditorWorkerHost } from 'vs/editor/common/services/editorWorkerHost'; import { StopWatch } from 'vs/base/common/stopwatch'; import { UnicodeTextModelHighlighter, UnicodeHighlighterOptions } from 'vs/editor/common/services/unicodeTextModelHighlighter'; -import { DiffComputer, IChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { DiffComputer, IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; import { linesDiffComputers } from 'vs/editor/common/diff/linesDiffComputers'; import { createProxyObject, getAllMethodNames } from 'vs/base/common/objects'; diff --git a/src/vs/editor/common/services/editorWorker.ts b/src/vs/editor/common/services/editorWorker.ts index 9038e313a9c..9e1cca8a460 100644 --- a/src/vs/editor/common/services/editorWorker.ts +++ b/src/vs/editor/common/services/editorWorker.ts @@ -6,7 +6,7 @@ import { URI } from 'vs/base/common/uri'; import { IRange } from 'vs/editor/common/core/range'; import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; -import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { IInplaceReplaceSupportResult, TextEdit } from 'vs/editor/common/languages'; import { UnicodeHighlighterOptions } from 'vs/editor/common/services/unicodeTextModelHighlighter'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/editor/test/common/diff/diffComputer.test.ts b/src/vs/editor/test/common/diff/diffComputer.test.ts index a8eb52b07c1..aca599dc6e5 100644 --- a/src/vs/editor/test/common/diff/diffComputer.test.ts +++ b/src/vs/editor/test/common/diff/diffComputer.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Constants } from 'vs/base/common/uint'; import { Range } from 'vs/editor/common/core/range'; -import { DiffComputer, ICharChange, ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { DiffComputer, ICharChange, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; diff --git a/src/vs/editor/test/common/services/testEditorWorkerService.ts b/src/vs/editor/test/common/services/testEditorWorkerService.ts index 640a3e4b596..e6693d821e9 100644 --- a/src/vs/editor/test/common/services/testEditorWorkerService.ts +++ b/src/vs/editor/test/common/services/testEditorWorkerService.ts @@ -8,7 +8,7 @@ import { IRange } from 'vs/editor/common/core/range'; import { DiffAlgorithmName, IEditorWorkerService, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker'; import { TextEdit, IInplaceReplaceSupportResult } from 'vs/editor/common/languages'; import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; -import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; export class TestEditorWorkerService implements IEditorWorkerService { diff --git a/src/vs/editor/test/node/diffing/diffingFixture.test.ts b/src/vs/editor/test/node/diffing/diffingFixture.test.ts index 59173290fc8..a04f2edf9d8 100644 --- a/src/vs/editor/test/node/diffing/diffingFixture.test.ts +++ b/src/vs/editor/test/node/diffing/diffingFixture.test.ts @@ -9,8 +9,8 @@ import { join, resolve } from 'path'; import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { FileAccess } from 'vs/base/common/network'; import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; -import { SmartLinesDiffComputer } from 'vs/editor/common/diff/smartLinesDiffComputer'; -import { StandardLinesDiffComputer } from 'vs/editor/common/diff/standardLinesDiffComputer'; +import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; +import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; suite('diff fixtures', () => { setup(() => { @@ -38,7 +38,7 @@ suite('diff fixtures', () => { const secondContent = readFileSync(join(folderPath, secondFileName), 'utf8').replaceAll('\r\n', '\n').replaceAll('\r', '\n'); const secondContentLines = secondContent.split(/\n/); - const diffingAlgo = diffingAlgoName === 'legacy' ? new SmartLinesDiffComputer() : new StandardLinesDiffComputer(); + const diffingAlgo = diffingAlgoName === 'legacy' ? new LegacyLinesDiffComputer() : new AdvancedLinesDiffComputer(); const ignoreTrimWhitespace = folder.indexOf('trimws') >= 0; const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }); diff --git a/src/vs/editor/test/node/diffing/lineRangeMapping.test.ts b/src/vs/editor/test/node/diffing/lineRangeMapping.test.ts index 5cda2c94794..f0d0802912d 100644 --- a/src/vs/editor/test/node/diffing/lineRangeMapping.test.ts +++ b/src/vs/editor/test/node/diffing/lineRangeMapping.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { RangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; -import { getLineRangeMapping } from 'vs/editor/common/diff/standardLinesDiffComputer'; +import { getLineRangeMapping } from 'vs/editor/common/diff/advancedLinesDiffComputer'; suite('lineRangeMapping', () => { test('1', () => { diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index 552241c27d7..b5e8bb0ef56 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -23,7 +23,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { IEditorControl } from 'vs/workbench/common/editor'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e4964fb5670..82646f45cac 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -19,7 +19,7 @@ import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import * as languages from 'vs/editor/common/languages'; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index e01a5c37361..90a0451aae1 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -15,7 +15,7 @@ import { basename } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { ISplice } from 'vs/base/common/sequence'; import { URI, UriComponents } from 'vs/base/common/uri'; -import { ILineChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { Command, WorkspaceEditMetadata } from 'vs/editor/common/languages'; import { IReadonlyTextBuffer } from 'vs/editor/common/model'; diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 41ac8d05a2b..589c0e5e99d 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -50,7 +50,7 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { onUnexpectedError } from 'vs/base/common/errors'; import { TextCompareEditorActiveContext } from 'vs/workbench/common/contextkeys'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { IChange } from 'vs/editor/common/diff/smartLinesDiffComputer'; +import { IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { Color } from 'vs/base/common/color'; import { ResourceMap } from 'vs/base/common/map'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; From 905931a8683a6e4e51e15ce4f116aae976752eb7 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 11:29:53 +0200 Subject: [PATCH 146/607] Fixes CI --- build/monaco/monaco.d.ts.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index ff4febe8ce6..89c884f18a0 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -107,7 +107,7 @@ export interface ICommandHandler { #include(vs/editor/common/core/editOperation): ISingleEditOperation #include(vs/editor/common/core/wordHelper): IWordAtPosition #includeAll(vs/editor/common/model): IScrollEvent -#include(vs/editor/common/diff/smartLinesDiffComputer): IChange, ICharChange, ILineChange +#include(vs/editor/common/diff/legacyLinesDiffComputer): IChange, ICharChange, ILineChange #include(vs/editor/common/diff/documentDiffProvider): IDocumentDiffProvider, IDocumentDiffProviderOptions, IDocumentDiff #include(vs/editor/common/core/lineRange): LineRange #include(vs/editor/common/diff/linesDiffComputer): LineRangeMapping, RangeMapping, MovedText, SimpleLineRangeMapping From 3354a63b539066d3df6c1d1295cf377eb96c7035 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 24 Aug 2023 16:33:22 +0200 Subject: [PATCH 147/607] regitsering the folding store and clearing it instead of disposing it --- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 449df81753f..6d665ce6484 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -84,6 +84,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { updateScrollLeftPosition(); this._updateWidgetWidth(); })); + this._register(this._foldingIconStore); updateScrollLeftPosition(); this._register(this._editor.onDidLayoutChange((e) => { @@ -136,10 +137,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private async _renderRootNode(): Promise { + this._foldingIconStore.clear(); if (!this._editor._getViewModel()) { return; } - this._foldingIconStore.dispose(); const foldingModel = await FoldingController.get(this._editor)?.getFoldingModel(); const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { From 67a99e361e0f69a662b415b3f65b49dfe6eb4b68 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 07:58:22 -0700 Subject: [PATCH 148/607] TerminalExecuteCommandEvent proposal Part of #145234 --- .eslintrc.json | 1 + .../common/extensionsApiProposals.ts | 1 + ....proposed.terminalExecuteCommandEvent.d.ts | 50 +++++++++++++++++++ 3 files changed, 52 insertions(+) create mode 100644 src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts diff --git a/.eslintrc.json b/.eslintrc.json index e6011aeed86..0644079623d 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -172,6 +172,7 @@ "drop", "edit", "end", + "execute", "expand", "grant", "hide", diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 741b9869e77..8ab8dfd5b31 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -89,6 +89,7 @@ export const allApiProposals = Object.freeze({ terminalContextMenu: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalContextMenu.d.ts', terminalDataWriteEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts', terminalDimensions: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDimensions.d.ts', + terminalExecuteCommandEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts', terminalQuickFixProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalQuickFixProvider.d.ts', terminalSelection: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalSelection.d.ts', testCoverage: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testCoverage.d.ts', diff --git a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts new file mode 100644 index 00000000000..09f5a6212e2 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * 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/145234 + + export interface TerminalExecuteCommandEvent { + terminal: Terminal; + command: TerminalCommand; + } + + export interface TerminalCommand { + /** + * The full command line that was executed, including both the command and the arguments. + */ + commandLine: string; + /** + * The current working directory that was reported by the shell. This will be a {@link Uri} + * if the string reported by the shell can reliably be mapped to the connected machine. + */ + cwd: Uri | string | undefined; + /** + * The result of the command. + */ + result: Thenable; + } + + export interface TerminalExecuteCommandResult { + /** + * The exit code reported by the shell. + */ + exitCode: number; + /** + * The output of the command when it has finished executing. This is the plain text shown in + * the terminal buffer and does not include raw escape sequences.. + */ + output: string; + } + + export namespace window { + /** + * An event that is emitted when a terminal with shell integration activated executes a + * command. + */ + export const onWillExecuteTerminalCommand: Event; + } +} From 1f9f663d1d0329e8e8b6e79bc73d3f237e5a87b8 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 13:13:28 +0200 Subject: [PATCH 149/607] Fixes #189327 --- .../diffEditorWidget2/diffEditorViewModel.ts | 13 +++++++++++-- .../widget/workerBasedDocumentDiffProvider.ts | 15 +++++++++++++-- src/vs/editor/common/diff/documentDiffProvider.ts | 3 ++- src/vs/monaco.d.ts | 2 +- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 7927623f49b..a41ba6a9909 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { RunOnceScheduler } from 'vs/base/common/async'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { isDefined } from 'vs/base/common/types'; import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; @@ -19,6 +19,7 @@ import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextMod import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; import { DiffEditorOptions } from './diffEditorOptions'; import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; export class DiffEditorViewModel extends Disposable implements IDiffEditorViewModel { private readonly _isDiffUpToDate = observableValue('isDiffUpToDate', false); @@ -64,6 +65,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo this._hoveredMovedText.set(movedText, undefined); } + private readonly _cancellationTokenSource = new CancellationTokenSource(); + constructor( public readonly model: IDiffEditorModel, private readonly _options: DiffEditorOptions, @@ -71,6 +74,8 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo ) { super(); + this._register(toDisposable(() => this._cancellationTokenSource.cancel())); + const contentChangedSignal = observableSignal('contentChangedSignal'); const debouncer = this._register(new RunOnceScheduler(() => contentChangedSignal.trigger(undefined), 200)); @@ -182,7 +187,11 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo ignoreTrimWhitespace: this._options.ignoreTrimWhitespace.read(reader), maxComputationTimeMs: this._options.maxComputationTimeMs.read(reader), computeMoves: this._options.showMoves.read(reader), - }); + }, this._cancellationTokenSource.token); + + if (this._cancellationTokenSource.token.isCancellationRequested) { + return; + } result = applyOriginalEdits(result, originalTextEditInfos, model.original, model.modified) ?? result; result = applyModifiedEdits(result, modifiedTextEditInfos, model.original, model.modified) ?? result; diff --git a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts index cca1f5ab297..648ba44763c 100644 --- a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts +++ b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; @@ -34,9 +35,9 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I this.diffAlgorithmOnDidChangeSubscription?.dispose(); } - async computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions): Promise { + async computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions, cancellationToken: CancellationToken): Promise { if (typeof this.diffAlgorithm !== 'string') { - return this.diffAlgorithm.computeDiff(original, modified, options); + return this.diffAlgorithm.computeDiff(original, modified, options, cancellationToken); } // This significantly speeds up the case when the original file is empty @@ -95,6 +96,16 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I timedOut: result?.quitEarly ?? true, }); + if (cancellationToken.isCancellationRequested) { + // Text models might be disposed! + return { + changes: [], + identical: false, + quitEarly: true, + moves: [], + }; + } + if (!result) { throw new Error('no diff result available'); } diff --git a/src/vs/editor/common/diff/documentDiffProvider.ts b/src/vs/editor/common/diff/documentDiffProvider.ts index ad707d114b5..02907dddca3 100644 --- a/src/vs/editor/common/diff/documentDiffProvider.ts +++ b/src/vs/editor/common/diff/documentDiffProvider.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { LineRangeMapping, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; import { ITextModel } from 'vs/editor/common/model'; @@ -14,7 +15,7 @@ export interface IDocumentDiffProvider { /** * Computes the diff between the text models `original` and `modified`. */ - computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions): Promise; + computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions, cancellationToken: CancellationToken): Promise; /** * Is fired when settings of the diff algorithm change that could alter the result of the diffing computation. diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 544197938c2..2d030c3fa23 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2374,7 +2374,7 @@ declare namespace monaco.editor { /** * Computes the diff between the text models `original` and `modified`. */ - computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions): Promise; + computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions, cancellationToken: CancellationToken): Promise; /** * Is fired when settings of the diff algorithm change that could alter the result of the diffing computation. * Any user of this provider should recompute the diff when this event is fired. From 4d9f1fc9c92bde758245ed28aa3a8389d5f4d745 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 14:37:09 +0200 Subject: [PATCH 150/607] Fixes CI --- src/vs/editor/browser/widget/diffEditorWidget.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index e1a43acbaf6..5588ae6f5ec 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -11,6 +11,7 @@ import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor import { IBoundarySashes, ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; import * as assert from 'vs/base/common/assert'; import { RunOnceScheduler } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -1186,7 +1187,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE ignoreTrimWhitespace: this._options.ignoreTrimWhitespace, maxComputationTimeMs: this._options.maxComputationTime, computeMoves: false, - }).then(result => { + }, CancellationToken.None).then(result => { if (currentToken === this._diffComputationToken && currentOriginalModel === this._originalEditor.getModel() && currentModifiedModel === this._modifiedEditor.getModel() From 94ad4e5e1fb341c48ebda57b786e5785f0338e85 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 08:13:35 -0700 Subject: [PATCH 151/607] address feedback --- .../terminal.accessibility.contribution.ts | 10 +---- .../browser/terminalAccessibleWidget.ts | 40 +++++++++---------- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 4eb13c439a6..96b9602e45d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -48,7 +48,7 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr } registerTerminalContribution(TextAreaSyncContribution.ID, TextAreaSyncContribution); -export class AccessibleBufferContribution extends DisposableStore implements ITerminalContribution { +class AccessibleBufferContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.accessible-buffer'; private _xterm: IXtermTerminal & { raw: Terminal } | undefined; static get(instance: ITerminalInstance): AccessibleBufferContribution | null { @@ -220,13 +220,7 @@ registerTerminalAction({ precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), run: async (c) => { const instance = c.service.activeInstance || await c.service.createTerminal({ location: TerminalLocation.Panel }); - if (!instance) { - return; - } - const contribution = instance.getContribution('terminal.accessible-buffer'); - if (contribution) { - contribution.hide(); - } + instance.getContribution(AccessibleBufferContribution.ID)?.hide(); instance.focus(true); } }); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts index 77fd6173934..f25793c1da9 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts @@ -40,16 +40,16 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { protected _listeners: IDisposable[] = []; - private readonly _focusedContextKey?: IContextKey; - private readonly _focusedLastLineContextKey?: IContextKey; + private readonly _focusedContextKey: IContextKey; + private readonly _focusedLastLineContextKey: IContextKey; private readonly _focusTracker?: dom.IFocusTracker; constructor( private readonly _className: string, protected readonly _instance: Pick, protected readonly _xterm: Pick & { raw: Terminal }, - private _focusContextKey: RawContextKey | undefined, - private _focusLastLineContextKey: RawContextKey | undefined, + private rawFocusContextKey: RawContextKey, + private rawFocusLastLineContextKey: RawContextKey, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IModelService private readonly _modelService: IModelService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -89,23 +89,21 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { this._element.replaceChildren(this._editorContainer); this._xtermElement.insertAdjacentElement('beforebegin', this._element); - if (this._focusContextKey && this._focusLastLineContextKey) { - this._focusTracker = this.add(dom.trackFocus(this._editorContainer)); - this._focusedContextKey = this._focusContextKey.bindTo(this._contextKeyService); - this._focusedLastLineContextKey = this._focusLastLineContextKey.bindTo(this._contextKeyService); - this.add(this._focusTracker.onDidFocus(() => { - this._focusedContextKey?.set(true); - this._focusedLastLineContextKey?.set(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); - })); - this.add(this._focusTracker.onDidBlur(() => { - this._focusedContextKey?.reset(); - this._focusedLastLineContextKey?.reset(); - })); - this._editorWidget.onDidChangeCursorPosition(() => { - console.log(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); - this._focusedLastLineContextKey?.set(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); - }); - } + this._focusTracker = this.add(dom.trackFocus(this._editorContainer)); + this._focusedContextKey = this.rawFocusContextKey.bindTo(this._contextKeyService); + this._focusedLastLineContextKey = this.rawFocusLastLineContextKey.bindTo(this._contextKeyService); + this.add(this._focusTracker.onDidFocus(() => { + this._focusedContextKey?.set(true); + this._focusedLastLineContextKey?.set(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); + })); + this.add(this._focusTracker.onDidBlur(() => { + this._focusedContextKey?.reset(); + this._focusedLastLineContextKey?.reset(); + })); + this._editorWidget.onDidChangeCursorPosition(() => { + console.log(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); + this._focusedLastLineContextKey?.set(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); + }); this.add(Event.runAndSubscribe(this._xterm.raw.onResize, () => this.layout())); this.add(this._configurationService.onDidChangeConfiguration(e => { From d1173ce59c051da9dd551a16b31d628d20a267be Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 08:28:10 -0700 Subject: [PATCH 152/607] rm private --- .../accessibility/browser/terminalAccessibleWidget.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts index f25793c1da9..b95d8814f1e 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts @@ -48,8 +48,8 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { private readonly _className: string, protected readonly _instance: Pick, protected readonly _xterm: Pick & { raw: Terminal }, - private rawFocusContextKey: RawContextKey, - private rawFocusLastLineContextKey: RawContextKey, + rawFocusContextKey: RawContextKey, + rawFocusLastLineContextKey: RawContextKey, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IModelService private readonly _modelService: IModelService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -90,8 +90,8 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { this._xtermElement.insertAdjacentElement('beforebegin', this._element); this._focusTracker = this.add(dom.trackFocus(this._editorContainer)); - this._focusedContextKey = this.rawFocusContextKey.bindTo(this._contextKeyService); - this._focusedLastLineContextKey = this.rawFocusLastLineContextKey.bindTo(this._contextKeyService); + this._focusedContextKey = rawFocusContextKey.bindTo(this._contextKeyService); + this._focusedLastLineContextKey = rawFocusLastLineContextKey.bindTo(this._contextKeyService); this.add(this._focusTracker.onDidFocus(() => { this._focusedContextKey?.set(true); this._focusedLastLineContextKey?.set(this._editorWidget.getSelection()?.positionLineNumber === this._editorWidget.getModel()?.getLineCount()); From 52771a40c46c2c6e982acf211c7337e7d4b36079 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 08:34:39 -0700 Subject: [PATCH 153/607] reuse var --- .../accessibility/browser/terminalAccessibleBuffer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts index 0835367c86e..f8fc9a2c500 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts @@ -110,8 +110,9 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { } private _getCommandsWithEditorLine(): ICommandWithEditorLine[] | undefined { - const commands = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.commands; - const currentCommand = this._instance.capabilities.get(TerminalCapability.CommandDetection)?.currentCommand; + const capability = this._instance.capabilities.get(TerminalCapability.CommandDetection); + const commands = capability?.commands; + const currentCommand = capability?.currentCommand; if (!commands?.length) { return; } From c0655c7c1725ad4c3494686f7be925838810f97d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 09:05:28 -0700 Subject: [PATCH 154/607] fix #188329 --- .../browser/terminal.accessibility.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 96b9602e45d..0efe30d072d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -117,8 +117,8 @@ registerTerminalAction({ precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), keybinding: [ { - primary: KeyMod.Shift | KeyCode.Tab, - secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.Alt | KeyCode.F2], + primary: KeyMod.CtrlCmd | KeyCode.UpArrow, + secondary: [KeyMod.Alt | KeyCode.F2], weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.focus, ContextKeyExpr.or(terminalTabFocusModeContextKey, TerminalContextKeys.accessibleBufferFocus.negate())) } From f52b2d78e9f4a2b564a465fb0ba9e63f7622b513 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 09:09:59 -0700 Subject: [PATCH 155/607] Revert "fix #188329" This reverts commit c0655c7c1725ad4c3494686f7be925838810f97d. --- .../browser/terminal.accessibility.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 0efe30d072d..96b9602e45d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -117,8 +117,8 @@ registerTerminalAction({ precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), keybinding: [ { - primary: KeyMod.CtrlCmd | KeyCode.UpArrow, - secondary: [KeyMod.Alt | KeyCode.F2], + primary: KeyMod.Shift | KeyCode.Tab, + secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.Alt | KeyCode.F2], weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.focus, ContextKeyExpr.or(terminalTabFocusModeContextKey, TerminalContextKeys.accessibleBufferFocus.negate())) } From 3bed7cfbdbde8c3370e040b9c8b8d9462a0c23b3 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 16:57:19 +0200 Subject: [PATCH 156/607] Fixes #190422 --- .../inlineCompletions/browser/commands.ts | 3 +- .../browser/inlineCompletionsHintsWidget.css | 2 +- .../browser/inlineCompletionsHintsWidget.ts | 56 ++++++++++++++----- 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts index 799433568e0..ac7535f9231 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/commands.ts @@ -148,7 +148,8 @@ export class AcceptInlineCompletion extends EditorAction { InlineCompletionContextKeys.inlineSuggestionVisible, EditorContextKeys.tabMovesFocus.toNegated(), InlineCompletionContextKeys.inlineSuggestionHasIndentationLessThanTabSize, - SuggestContext.Visible.toNegated() + SuggestContext.Visible.toNegated(), + EditorContextKeys.hoverFocused.toNegated(), ), } }); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.css b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.css index 642e6c51d7a..196307cc49e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.css +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.css @@ -29,7 +29,7 @@ padding: 2px 3px; } -.monaco-editor .inlineSuggestionsHints .custom-actions .action-item:nth-child(2) a { +.monaco-editor .inlineSuggestionsHints .availableSuggestionCount a { display: flex; min-width: 19px; justify-content: center; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts index eb9aa10b05c..0cde6e9a9b5 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { h } from 'vs/base/browser/dom'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { KeybindingLabel, unthemedKeybindingLabelOptions } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { Action, IAction, Separator } from 'vs/base/common/actions'; import { equals } from 'vs/base/common/arrays'; @@ -112,10 +112,7 @@ export class InlineSuggestionHintsContentWidget extends Disposable implements IC public readonly suppressMouseDown = false; private readonly nodes = h('div.inlineSuggestionsHints', { className: this.withBorder ? '.withBorder' : '' }, [ - h('div', { style: { display: 'flex' } }, [ - h('div@actionBar', { className: 'custom-actions' }), - h('div@toolBar'), - ]) + h('div@toolBar'), ]); private createCommandAction(commandId: string, label: string, iconClassName: string): Action { @@ -173,21 +170,29 @@ export class InlineSuggestionHintsContentWidget extends Disposable implements IC ) { super(); - const actionBar = this._register(new ActionBar(this.nodes.actionBar)); - - actionBar.push(this.previousAction, { icon: true, label: false }); - actionBar.push(this.availableSuggestionCountAction); - actionBar.push(this.nextAction, { icon: true, label: false }); - this.toolBar = this._register(instantiationService.createInstance(CustomizedMenuWorkbenchToolBar, this.nodes.toolBar, MenuId.InlineSuggestionToolbar, { menuOptions: { renderShortTitle: true }, toolbarOptions: { primaryGroup: g => g.startsWith('primary') }, actionViewItemProvider: (action, options) => { - return action instanceof MenuItemAction ? instantiationService.createInstance(StatusBarViewItem, action, undefined) : undefined; + if (action instanceof MenuItemAction) { + return instantiationService.createInstance(StatusBarViewItem, action, undefined); + } + if (action === this.availableSuggestionCountAction) { + const a = new ActionViewItemWithClassName(undefined, action, { label: true, icon: false }); + a.setClass('availableSuggestionCount'); + return a; + } + return undefined; }, telemetrySource: 'InlineSuggestionToolbar', })); + this.toolBar.setPrependedPrimaryActions([ + this.previousAction, + this.availableSuggestionCountAction, + this.nextAction, + ]); + this._register(this.toolBar.onDidChangeDropdownVisibility(e => { InlineSuggestionHintsContentWidget._dropDownVisible = e; })); @@ -270,6 +275,21 @@ export class InlineSuggestionHintsContentWidget extends Disposable implements IC } } +class ActionViewItemWithClassName extends ActionViewItem { + private _className: string | undefined = undefined; + + setClass(className: string | undefined): void { + this._className = className; + } + + override render(container: HTMLElement): void { + super.render(container); + if (this._className) { + container.classList.add(this._className); + } + } +} + class StatusBarViewItem extends MenuEntryActionViewItem { protected override updateLabel() { const kb = this._keybindingService.lookupKeybinding(this._action.id, this._contextKeyService); @@ -291,6 +311,7 @@ class StatusBarViewItem extends MenuEntryActionViewItem { export class CustomizedMenuWorkbenchToolBar extends WorkbenchToolBar { private readonly menu = this._store.add(this.menuService.createMenu(this.menuId, this.contextKeyService, { emitEventsForSubmenuChanges: true })); private additionalActions: IAction[] = []; + private prependedPrimaryActions: IAction[] = []; constructor( container: HTMLElement, @@ -319,12 +340,21 @@ export class CustomizedMenuWorkbenchToolBar extends WorkbenchToolBar { ); secondary.push(...this.additionalActions); + primary.unshift(...this.prependedPrimaryActions); this.setActions(primary, secondary); } + setPrependedPrimaryActions(actions: IAction[]): void { + if (equals(this.prependedPrimaryActions, actions, (a, b) => a === b)) { + return; + } + + this.prependedPrimaryActions = actions; + this.updateToolbar(); + } + setAdditionalSecondaryActions(actions: IAction[]): void { if (equals(this.additionalActions, actions, (a, b) => a === b)) { - // don't update if the actions are the same return; } From 3fbf0442d354acd412eaee43ef8ba90bfd9ac4cb Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 24 Aug 2023 09:44:27 -0700 Subject: [PATCH 157/607] tunnels: fix forgotten event listener (#191228) Was accidentally removed removed during a PR comment followup Fixes #190859 --- src/vs/platform/tunnel/node/tunnelService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/platform/tunnel/node/tunnelService.ts b/src/vs/platform/tunnel/node/tunnelService.ts index 0894400360d..f28dc9e2271 100644 --- a/src/vs/platform/tunnel/node/tunnelService.ts +++ b/src/vs/platform/tunnel/node/tunnelService.ts @@ -19,6 +19,7 @@ import { IAddressProvider, IConnectionOptions, connectRemoteAgentTunnel } from ' import { IRemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { AbstractTunnelService, ISharedTunnelsService, ITunnelProvider, ITunnelService, RemoteTunnel, TunnelPrivacyId, isAllInterfaces, isLocalhost, isPortPrivileged, isTunnelProvider } from 'vs/platform/tunnel/common/tunnel'; +import { VSBuffer } from 'vs/base/common/buffer'; async function createRemoteTunnel(options: IConnectionOptions, defaultTunnelHost: string, tunnelRemoteHost: string, tunnelRemotePort: number, tunnelLocalPort?: number): Promise { let readyTunnel: NodeRemoteTunnel | undefined; @@ -159,6 +160,7 @@ export class NodeRemoteTunnel extends Disposable implements RemoteTunnel { remoteSocket.onClose(() => localSocket.destroy()); remoteSocket.onEnd(() => localSocket.end()); remoteSocket.onData(d => localSocket.write(d.buffer)); + localSocket.on('data', d => remoteSocket.write(VSBuffer.wrap(d))); localSocket.resume(); } From 6acb0ecd042d3db44604497e3ab82644bd0787e7 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 18:53:22 +0200 Subject: [PATCH 158/607] Fixes #188070 --- .../threadedBackgroundTokenizerFactory.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/services/textMate/browser/backgroundTokenization/threadedBackgroundTokenizerFactory.ts b/src/vs/workbench/services/textMate/browser/backgroundTokenization/threadedBackgroundTokenizerFactory.ts index b121441fd78..9558ffdd75e 100644 --- a/src/vs/workbench/services/textMate/browser/backgroundTokenization/threadedBackgroundTokenizerFactory.ts +++ b/src/vs/workbench/services/textMate/browser/backgroundTokenization/threadedBackgroundTokenizerFactory.ts @@ -63,7 +63,7 @@ export class ThreadedBackgroundTokenizerFactory implements IDisposable { const controllerContainer = this._getWorkerProxy().then((workerProxy) => { if (store.isDisposed || !workerProxy) { return undefined; } - const controllerContainer = { controller: undefined as undefined | TextMateWorkerTokenizerController }; + const controllerContainer = { controller: undefined as undefined | TextMateWorkerTokenizerController, worker: this._worker }; store.add(keepAliveWhenAttached(textModel, () => { const controller = new TextMateWorkerTokenizerController(textModel, workerProxy, this._languageService.languageIdCodec, tokenStore, this._configurationService, maxTokenizationLineLength); controllerContainer.controller = controller; @@ -82,10 +82,12 @@ export class ThreadedBackgroundTokenizerFactory implements IDisposable { store.dispose(); }, requestTokens: async (startLineNumber, endLineNumberExclusive) => { - const controller = (await controllerContainer)?.controller; - if (controller) { - // If there is no controller, the model has been detached in the meantime - controller.requestTokens(startLineNumber, endLineNumberExclusive); + const container = await controllerContainer; + + // If there is no controller, the model has been detached in the meantime. + // Only request the proxy object if the worker is the same! + if (container?.controller && container.worker === this._worker) { + container.controller.requestTokens(startLineNumber, endLineNumberExclusive); } }, reportMismatchingTokens: (lineNumber) => { From bb90524c2c23b78242e9eaa1b1177d23b7a2e0e2 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 19:14:09 +0200 Subject: [PATCH 159/607] Fixes #187839 --- .../widget/diffEditorWidget2/diffEditorWidget2.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 280c2bb7b9a..b0da2fb6191 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -45,6 +45,7 @@ import { DiffEditorEditors } from './diffEditorEditors'; import { DiffEditorOptions } from './diffEditorOptions'; import { DiffEditorViewModel, DiffMapping, DiffState } from './diffEditorViewModel'; import { toDisposable } from 'vs/base/common/lifecycle'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly elements = h('div.monaco-diff-editor.side-by-side', { style: { position: 'relative', height: '100%' } }, [ @@ -89,6 +90,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { @IInstantiationService private readonly _parentInstantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, @IAudioCueService private readonly _audioCueService: IAudioCueService, + @IEditorProgressService private readonly _editorProgressService: IEditorProgressService, ) { super(); codeEditorService.willCreateDiffEditor(); @@ -262,6 +264,14 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { } } })); + + const isDiffUpToDate = this._diffModel.map((m, reader) => m?.isDiffUpToDate.read(reader)); + this._register(autorunWithStore((reader, store) => { + if (isDiffUpToDate.read(reader) === false) { + const r = this._editorProgressService.show(true, 1000); + store.add(toDisposable(() => r.done())); + } + })); } public getContentHeight() { From 121635a5ca9ff97dbe8abbe04d6b1b4d95bcb691 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 10:26:08 -0700 Subject: [PATCH 160/607] Tweak api --- ....proposed.terminalExecuteCommandEvent.d.ts | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts index 09f5a6212e2..68391fc7293 100644 --- a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts @@ -9,10 +9,15 @@ declare module 'vscode' { export interface TerminalExecuteCommandEvent { terminal: Terminal; - command: TerminalCommand; + command: TerminalCommand; } - export interface TerminalCommand { + // NOTE: The generic here is for the future executeCommand function where the result isn't available. + export interface TerminalCommand> { + /** + * The {@link Terminal} the command was executed in. + */ + terminal: Terminal; /** * The full command line that was executed, including both the command and the arguments. */ @@ -25,7 +30,7 @@ declare module 'vscode' { /** * The result of the command. */ - result: Thenable; + result: T; } export interface TerminalExecuteCommandResult { @@ -42,9 +47,14 @@ declare module 'vscode' { export namespace window { /** - * An event that is emitted when a terminal with shell integration activated executes a - * command. + * An event that is emitted when a terminal with shell integration activated has started + * executing a command. */ - export const onWillExecuteTerminalCommand: Event; + export const onWillExecuteTerminalCommand: Event>; + /** + * An event that is emitted when a terminal with shell integration activated has completed + * executing a command. + */ + export const onDidExecuteTerminalCommand: Event; } } From ae9df9733c5690741bf88e9b0095bd00ed01a3c3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 10:37:56 -0700 Subject: [PATCH 161/607] Simplify events --- .../workbench/api/common/extHost.api.impl.ts | 9 ++++++ .../api/common/extHostTerminalService.ts | 31 +++++++++++-------- ....proposed.terminalExecuteCommandEvent.d.ts | 10 ++++-- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index acdb35b1b1e..e04d6de10a3 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -698,6 +698,15 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'terminalDataWriteEvent'); return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables); }, + onWillExecuteTerminalCommand(listener, thisArg?, disposables?) { + checkProposedApiEnabled(extension, 'terminalExecuteCommandEvent'); + return extHostTerminalService.onWillExecuteTerminalCommand(listener, thisArg, disposables); + + }, + onDidExecuteTerminalCommand(listener, thisArg?, disposables?) { + checkProposedApiEnabled(extension, 'terminalExecuteCommandEvent'); + return extHostTerminalService.onDidExecuteTerminalCommand(listener, thisArg, disposables); + }, get state() { return extHostWindow.getState(extension); }, diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 84f2cac70c1..92d2e7ebf62 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -34,13 +34,15 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID activeTerminal: vscode.Terminal | undefined; terminals: vscode.Terminal[]; - onDidCloseTerminal: Event; - onDidOpenTerminal: Event; - onDidChangeActiveTerminal: Event; - onDidChangeTerminalDimensions: Event; - onDidChangeTerminalState: Event; - onDidWriteTerminalData: Event; - onDidChangeShell: Event; + readonly onDidCloseTerminal: Event; + readonly onDidOpenTerminal: Event; + readonly onDidChangeActiveTerminal: Event; + readonly onDidChangeTerminalDimensions: Event; + readonly onDidChangeTerminalState: Event; + readonly onDidWriteTerminalData: Event; + readonly onWillExecuteTerminalCommand: Event; + readonly onDidExecuteTerminalCommand: Event; + readonly onDidChangeShell: Event; createTerminal(name?: string, shellPath?: string, shellArgs?: readonly string[] | string): vscode.Terminal; createTerminalFromOptions(options: vscode.TerminalOptions, internalOptions?: ITerminalInternalOptions): vscode.Terminal; @@ -393,8 +395,15 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I readonly onDidChangeTerminalDimensions = this._onDidChangeTerminalDimensions.event; protected readonly _onDidChangeTerminalState = new Emitter(); readonly onDidChangeTerminalState = this._onDidChangeTerminalState.event; - protected readonly _onDidWriteTerminalData: Emitter; - get onDidWriteTerminalData(): Event { return this._onDidWriteTerminalData.event; } + protected readonly _onDidWriteTerminalData: Emitter = new Emitter({ + onWillAddFirstListener: () => this._proxy.$startSendingDataEvents(), + onDidRemoveLastListener: () => this._proxy.$stopSendingDataEvents() + }); + readonly onDidWriteTerminalData = this._onDidWriteTerminalData.event; + protected readonly _onWillExecuteCommand = new Emitter(); + readonly onWillExecuteTerminalCommand = this._onWillExecuteCommand.event; + protected readonly _onDidExecuteCommand = new Emitter(); + readonly onDidExecuteTerminalCommand = this._onDidExecuteCommand.event; protected readonly _onDidChangeShell = new Emitter(); readonly onDidChangeShell = this._onDidChangeShell.event; @@ -406,10 +415,6 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I super(); this._proxy = extHostRpc.getProxy(MainContext.MainThreadTerminalService); this._bufferer = new TerminalDataBufferer(this._proxy.$sendProcessData); - this._onDidWriteTerminalData = new Emitter({ - onWillAddFirstListener: () => this._proxy.$startSendingDataEvents(), - onDidRemoveLastListener: () => this._proxy.$stopSendingDataEvents() - }); this._proxy.$registerProcessSupport(supportsProcesses); this._register({ dispose: () => { diff --git a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts index 68391fc7293..bfd2e10a196 100644 --- a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts @@ -7,13 +7,17 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/145234 + export interface TerminalWillExecuteCommandEvent { + terminal: Terminal; + command: TerminalCommand>; + } + export interface TerminalExecuteCommandEvent { terminal: Terminal; command: TerminalCommand; } - // NOTE: The generic here is for the future executeCommand function where the result isn't available. - export interface TerminalCommand> { + export interface TerminalCommand> { /** * The {@link Terminal} the command was executed in. */ @@ -50,7 +54,7 @@ declare module 'vscode' { * An event that is emitted when a terminal with shell integration activated has started * executing a command. */ - export const onWillExecuteTerminalCommand: Event>; + export const onWillExecuteTerminalCommand: Event; /** * An event that is emitted when a terminal with shell integration activated has completed * executing a command. From c520db561ce3ffbb9d9507f0a61f4cd375609c17 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 10:52:37 -0700 Subject: [PATCH 162/607] fix #189864 --- .../browser/accessibilityContributions.ts | 2 +- .../accessibility/browser/accessibleView.ts | 25 ++++++++++--------- .../browser/accessibleViewActions.ts | 4 +-- .../browser/actions/chatAccessibilityHelp.ts | 2 +- .../browser/accessibility/accessibility.css | 2 +- .../codeEditor/browser/diffEditorHelper.ts | 2 +- .../notebook/browser/notebookAccessibility.ts | 2 +- .../browser/terminalAccessibilityHelp.ts | 16 ++++++------ 8 files changed, 29 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 1baf82143d4..cfab22c7cc3 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -101,7 +101,7 @@ class AccessibilityHelpProvider implements IAccessibleContentProvider { } else { content.push(this._descriptionForCommand(ToggleTabFocusModeAction.ID, AccessibilityHelpNLS.tabFocusModeOffMsg, AccessibilityHelpNLS.tabFocusModeOffMsgNoKb)); } - return content.join('\n'); + return content.join('\n\n'); } } diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 68302f41952..617c87028f8 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -3,13 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { EventType, addDisposableListener } from 'vs/base/browser/dom'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert } from 'vs/base/browser/ui/aria/aria'; +import { IAction } from 'vs/base/common/actions'; +import { Codicon } from 'vs/base/common/codicons'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { marked } from 'vs/base/common/marked/marked'; import { isMacintosh } from 'vs/base/common/platform'; +import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; @@ -20,6 +24,7 @@ import { AccessibilityHelpNLS } from 'vs/editor/common/standaloneStrings'; import { CodeActionController } from 'vs/editor/contrib/codeAction/browser/codeActionController'; import { localize } from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -31,14 +36,9 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPickerQuickAccessItem } from 'vs/platform/quickinput/browser/pickerQuickAccess'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; -import { IAction } from 'vs/base/common/actions'; -import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { Codicon } from 'vs/base/common/codicons'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { addDisposableListener, EventType } from 'vs/base/browser/dom'; const enum DIMENSIONS { MAX_WIDTH = 600 @@ -108,6 +108,7 @@ class AccessibleView extends Disposable { private _editorContainer: HTMLElement; private _currentProvider: IAccessibleContentProvider | undefined; private readonly _toolbar: WorkbenchToolBar; + private _currentContent: string | undefined; constructor( @IOpenerService private readonly _openerService: IOpenerService, @@ -217,10 +218,10 @@ class AccessibleView extends Disposable { } getSymbols(): IAccessibleViewSymbol[] | undefined { - if (!this._currentProvider) { + if (!this._currentProvider || !this._currentContent) { return; } - const tokens = this._currentProvider.options.language && this._currentProvider.options.language !== 'markdown' ? this._currentProvider.getSymbols?.() : marked.lexer(this._currentProvider.provideContent()); + const tokens = this._currentProvider.options.language && this._currentProvider.options.language !== 'markdown' ? this._currentProvider.getSymbols?.() : marked.lexer(this._currentContent); if (!tokens) { return; } @@ -299,7 +300,7 @@ class AccessibleView extends Disposable { } this._updateContextKeys(provider, true); const value = this._configurationService.getValue(provider.verbositySettingKey); - const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\nPress H now to open a browser window with more information related to accessibility.\n") : ''; + const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\n\nPress H now to open a browser window with more information related to accessibility.\n\n") : ''; let disableHelpHint = ''; if (provider.options.type === AccessibleViewType.Help && !!value) { disableHelpHint = this._getDisableVerbosityHint(provider.verbositySettingKey); @@ -321,9 +322,9 @@ class AccessibleView extends Disposable { } } - const fragment = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', '\nExit this dialog via the Escape key.'); + this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', '\n\nExit this dialog via the Escape key.'); - this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment })).then((model) => { + this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment: this._currentContent })).then((model) => { if (!model) { return; } @@ -424,7 +425,7 @@ class AccessibleView extends Disposable { if (!this._currentProvider) { return false; } - return this._currentProvider.options.language === 'markdown' || this._currentProvider.options.language === undefined || !!this._currentProvider.getSymbols; + return this._currentProvider.options.type === AccessibleViewType.Help || this._currentProvider.options.language === 'markdown' || this._currentProvider.options.language === undefined || !!this._currentProvider.getSymbols; } public showAccessibleViewHelp(): void { diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index c62bcf7782b..9133dc3fd82 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -84,7 +84,7 @@ class AccessibleViewGoToSymbolAction extends Action2 { constructor() { super({ id: AccessibilityCommandId.GoToSymbol, - precondition: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewGoToSymbolSupported), + precondition: ContextKeyExpr.and(ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown), accessibleViewGoToSymbolSupported), keybinding: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyO, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Period], @@ -95,7 +95,7 @@ class AccessibleViewGoToSymbolAction extends Action2 { commandPalette, { ...accessibleViewMenu, - when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation), + when: ContextKeyExpr.and(ContextKeyExpr.or(accessibleViewIsShown, accessibilityHelpIsShown), accessibleViewGoToSymbolSupported), } ], title: localize('editor.action.accessibleViewGoToSymbol', "Go To Symbol in Accessible View") diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index f8c40b935f7..e277b372021 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -45,7 +45,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'pane content.push(localize('inlineChat.toolbar', "Use tab to reach conditional parts like commands, status, message responses and more.")); } content.push(localize('chat.audioCues', "Audio cues can be changed via settings with a prefix of audioCues.chat. By default, if a request takes more than 4 seconds, you will hear an audio cue indicating that progress is still occurring.")); - return content.join('\n'); + return content.join('\n\n'); } function descriptionForCommand(commandId: string, msg: string, noKbMsg: string, keybindingService: IKeybindingService): string { diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css index 25fcb4b03d8..2e0a1c51753 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css @@ -11,7 +11,7 @@ border: 2px solid var(--vscode-focusBorder); border-radius: 6px; margin-top: -1px; - z-index: 2550; + z-index: 2540; } .accessible-view-container .actions-container { diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index 1e0d06ff9b8..22148696b72 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -107,7 +107,7 @@ function createScreenReaderHelp(): IDisposable { localize('msg1', "You are in a diff editor."), localize('msg2', "Press {0} or {1} to view the next or previous diff in the diff review mode that is optimized for screen readers.", next, previous), localize('msg3', "To control which audio cues should be played, the following settings can be configured: {0}.", keys.join(', ')), - ].join('\n'), + ].join('\n\n'), onClose: () => { codeEditor.focus(); }, diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts index 790e9e8bdc3..a019db5693e 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts @@ -34,7 +34,7 @@ export function getAccessibilityHelpText(accessor: ServicesAccessor): string { content.push(localize('notebook.changeCellType', 'The Change Cell to Code/Markdown commands are used to switch between cell types.')); - return content.join('\n'); + return content.join('\n\n'); } function descriptionForCommand(commandId: string, msg: string, noKbMsg: string, keybindingService: IKeybindingService): string { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index d3a0e3a592c..2504d461fb2 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -75,18 +75,20 @@ export class TerminalAccessibleContentProvider extends Disposable implements IAc content.push(localize('commandPromptMigration', "Consider using powershell instead of command prompt for an improved experience")); } if (this._hasShellIntegration) { - content.push(localize('shellIntegration', "The terminal has a feature called shell integration that offers an enhanced experience and provides useful commands for screen readers such as:")); - content.push('- ' + this._descriptionForCommand(TerminalCommandId.AccessibleBufferGoToNextCommand, localize('goToNextCommand', 'Go to Next Command ({0})'), localize('goToNextCommandNoKb', 'Go to Next Command is currently not triggerable by a keybinding.'))); - content.push('- ' + this._descriptionForCommand(TerminalCommandId.AccessibleBufferGoToPreviousCommand, localize('goToPreviousCommand', 'Go to Previous Command ({0})'), localize('goToPreviousCommandNoKb', 'Go to Previous Command is currently not triggerable by a keybinding.'))); - content.push('- ' + this._descriptionForCommand(TerminalCommandId.NavigateAccessibleBuffer, localize('navigateAccessibleBuffer', 'Navigate Accessible Buffer ({0})'), localize('navigateAccessibleBufferNoKb', 'Navigate Accessible Buffer is currently not triggerable by a keybinding.'))); - content.push('- ' + this._descriptionForCommand(TerminalCommandId.RunRecentCommand, localize('runRecentCommand', 'Run Recent Command ({0})'), localize('runRecentCommandNoKb', 'Run Recent Command is currently not triggerable by a keybinding.'))); - content.push('- ' + this._descriptionForCommand(TerminalCommandId.GoToRecentDirectory, localize('goToRecentDirectory', 'Go to Recent Directory ({0})'), localize('goToRecentDirectoryNoKb', 'Go to Recent Directory is currently not triggerable by a keybinding.'))); + const shellIntegrationCommandList = []; + shellIntegrationCommandList.push(localize('shellIntegration', "The terminal has a feature called shell integration that offers an enhanced experience and provides useful commands for screen readers such as:")); + shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.AccessibleBufferGoToNextCommand, localize('goToNextCommand', 'Go to Next Command ({0})'), localize('goToNextCommandNoKb', 'Go to Next Command is currently not triggerable by a keybinding.'))); + shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.AccessibleBufferGoToPreviousCommand, localize('goToPreviousCommand', 'Go to Previous Command ({0})'), localize('goToPreviousCommandNoKb', 'Go to Previous Command is currently not triggerable by a keybinding.'))); + shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.NavigateAccessibleBuffer, localize('navigateAccessibleBuffer', 'Navigate Accessible Buffer ({0})'), localize('navigateAccessibleBufferNoKb', 'Navigate Accessible Buffer is currently not triggerable by a keybinding.'))); + shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.RunRecentCommand, localize('runRecentCommand', 'Run Recent Command ({0})'), localize('runRecentCommandNoKb', 'Run Recent Command is currently not triggerable by a keybinding.'))); + shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.GoToRecentDirectory, localize('goToRecentDirectory', 'Go to Recent Directory ({0})'), localize('goToRecentDirectoryNoKb', 'Go to Recent Directory is currently not triggerable by a keybinding.'))); + content.push(shellIntegrationCommandList.join('\n')); } else { content.push(this._descriptionForCommand(TerminalCommandId.RunRecentCommand, localize('goToRecentDirectoryNoShellIntegration', 'The Go to Recent Directory command ({0}) enables screen readers to easily navigate to a directory that has been used in the terminal.'), localize('goToRecentDirectoryNoKbNoShellIntegration', 'The Go to Recent Directory command enables screen readers to easily navigate to a directory that has been used in the terminal and is currently not triggerable by a keybinding.'))); } content.push(this._descriptionForCommand(TerminalCommandId.OpenDetectedLink, localize('openDetectedLink', 'The Open Detected Link ({0}) command enables screen readers to easily open links found in the terminal.'), localize('openDetectedLinkNoKb', 'The Open Detected Link command enables screen readers to easily open links found in the terminal and is currently not triggerable by a keybinding.'))); content.push(this._descriptionForCommand(TerminalCommandId.NewWithProfile, localize('newWithProfile', 'The Create New Terminal (With Profile) ({0}) command allows for easy terminal creation using a specific profile.'), localize('newWithProfileNoKb', 'The Create New Terminal (With Profile) command allows for easy terminal creation using a specific profile and is currently not triggerable by a keybinding.'))); content.push(localize('accessibilitySettings', 'Access accessibility settings such as `terminal.integrated.tabFocusMode` via the Preferences: Open Accessibility Settings command.')); - return content.join('\n'); + return content.join('\n\n'); } } From ac0d0a89c83d38cc090c70d3b9059239f4fd223f Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 24 Aug 2023 10:53:05 -0700 Subject: [PATCH 163/607] cli: adopt latest devtunnels for ipv6 forwarding support (#191236) --- cli/Cargo.lock | 2 +- cli/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index a67cc7cf3bd..34627da135b 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -2471,7 +2471,7 @@ dependencies = [ [[package]] name = "tunnels" version = "0.1.0" -source = "git+https://github.com/microsoft/dev-tunnels?rev=2621784a9ad72aa39500372391332a14bad581a3#2621784a9ad72aa39500372391332a14bad581a3" +source = "git+https://github.com/microsoft/dev-tunnels?rev=3141ad7be00e18c4231f7c4fb6c11f9219ac49af#3141ad7be00e18c4231f7c4fb6c11f9219ac49af" dependencies = [ "async-trait", "chrono", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 18f18069c1f..50fad68a8a4 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -34,7 +34,7 @@ serde_bytes = "0.11.9" chrono = { version = "0.4.26", features = ["serde", "std", "clock"], default-features = false } gethostname = "0.4.3" libc = "0.2.144" -tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "2621784a9ad72aa39500372391332a14bad581a3", default-features = false, features = ["connections"] } +tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "3141ad7be00e18c4231f7c4fb6c11f9219ac49af", default-features = false, features = ["connections"] } keyring = { version = "2.0.3", default-features = false, features = ["linux-secret-service-rt-tokio-crypto-openssl"] } dialoguer = "0.10.4" hyper = { version = "0.14.26", features = ["server", "http1", "runtime"] } From 7c205af18101b8b0a3325de3cda08792f8120715 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 10:57:28 -0700 Subject: [PATCH 164/607] make sure go to symbol works for help menu default actions --- .../contrib/accessibility/browser/accessibleView.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 617c87028f8..fd562e9e189 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -258,7 +258,10 @@ class AccessibleView extends Disposable { } showSymbol(provider: IAccessibleContentProvider, symbol: IAccessibleViewSymbol): void { - const index = provider.provideContent().split('\n').findIndex(line => line.includes(symbol.info.split('\n')[0]) || (symbol.firstListItem && line.includes(symbol.firstListItem))) ?? -1; + if (!this._currentContent) { + return; + } + const index = this._currentContent.split('\n').findIndex(line => line.includes(symbol.info.split('\n')[0]) || (symbol.firstListItem && line.includes(symbol.firstListItem))) ?? -1; if (index >= 0) { this.show(provider); this._editorWidget.revealLine(index + 1); From ff5ec02d95db113257519757b168dfa8c841c7db Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 11:05:40 -0700 Subject: [PATCH 165/607] move setting of context key to fix issue on first invocation --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 2 +- .../contrib/codeEditor/browser/accessibility/accessibility.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index fd562e9e189..33deac70738 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -301,7 +301,6 @@ class AccessibleView extends Disposable { this._currentProvider = provider; this._accessibleViewCurrentProviderId.set(provider.verbositySettingKey.replaceAll('accessibility.verbosity.', '')); } - this._updateContextKeys(provider, true); const value = this._configurationService.getValue(provider.verbositySettingKey); const readMoreLink = provider.options.readMoreUrl ? localize("openDoc", "\n\nPress H now to open a browser window with more information related to accessibility.\n\n") : ''; let disableHelpHint = ''; @@ -326,6 +325,7 @@ class AccessibleView extends Disposable { } this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', '\n\nExit this dialog via the Escape key.'); + this._updateContextKeys(provider, true); this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment: this._currentContent })).then((model) => { if (!model) { diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css index 2e0a1c51753..25fcb4b03d8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css @@ -11,7 +11,7 @@ border: 2px solid var(--vscode-focusBorder); border-radius: 6px; margin-top: -1px; - z-index: 2540; + z-index: 2550; } .accessible-view-container .actions-container { From 2af059e4a7b3fb1ea7a8c208a59b44cfb187c212 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:13:53 -0700 Subject: [PATCH 166/607] Hook up some of the command event --- .../api/browser/mainThreadTerminalService.ts | 21 +++++++++++ .../workbench/api/common/extHost.protocol.ts | 14 +++++-- .../api/common/extHostTerminalService.ts | 37 ++++++++++++++++--- ....proposed.terminalExecuteCommandEvent.d.ts | 2 +- 4 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 295cafe4de0..4a7e2b8f2be 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -228,6 +228,20 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._dataEventTracker.clear(); } + public $startSendingCommandEvents(): void { + // TODO: Impl + this._logService.info('$startSendingCommandEvents'); + setInterval(() => { + this._onWillExecuteCommand(this._terminalService.instances[0]!.instanceId, null); + this._onDidExecuteCommand(this._terminalService.instances[0]!.instanceId, null); + }, 3000); + } + + public $stopSendingCommandEvents(): void { + this._logService.info('$stopSendingCommandEvents'); + // TODO: Impl + } + public $startLinkProvider(): void { this._linkProvider?.dispose(); this._linkProvider = this._terminalLinkProviderService.registerLinkProvider(new ExtensionTerminalLinkProvider(this._proxy)); @@ -306,6 +320,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._proxy.$acceptTerminalProcessData(terminalId, data); } + private _onWillExecuteCommand(terminalId: number, command: any): void { + this._proxy.$acceptWillExecuteCommand(terminalId, command); + } + private _onDidExecuteCommand(terminalId: number, command: any): void { + this._proxy.$acceptDidExecuteCommand(terminalId, command); + } + private _onTitleChanged(terminalId: number, name: string): void { this._proxy.$acceptTerminalTitleChange(terminalId, name); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 82646f45cac..1d575b97962 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -509,10 +509,6 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $hide(id: ExtHostTerminalIdentifier): void; $sendText(id: ExtHostTerminalIdentifier, text: string, addNewLine: boolean): void; $show(id: ExtHostTerminalIdentifier, preserveFocus: boolean): void; - $startSendingDataEvents(): void; - $stopSendingDataEvents(): void; - $startLinkProvider(): void; - $stopLinkProvider(): void; $registerProcessSupport(isSupported: boolean): void; $registerProfileProvider(id: string, extensionIdentifier: string): void; $unregisterProfileProvider(id: string): void; @@ -520,6 +516,14 @@ export interface MainThreadTerminalServiceShape extends IDisposable { $unregisterQuickFixProvider(id: string): void; $setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined, descriptionMap: ISerializableEnvironmentDescriptionMap): void; + // Optional event toggles + $startSendingDataEvents(): void; + $stopSendingDataEvents(): void; + $startSendingCommandEvents(): void; + $stopSendingCommandEvents(): void; + $startLinkProvider(): void; + $stopLinkProvider(): void; + // Process $sendProcessData(terminalId: number, data: string): void; $sendProcessReady(terminalId: number, pid: number, cwd: string, windowsPty: IProcessReadyWindowsPty | undefined): void; @@ -2093,6 +2097,8 @@ export interface ExtHostTerminalServiceShape { $acceptActiveTerminalChanged(id: number | null): void; $acceptTerminalProcessId(id: number, processId: number): void; $acceptTerminalProcessData(id: number, data: string): void; + $acceptWillExecuteCommand(id: number, command: any): void; + $acceptDidExecuteCommand(id: number, command: any): void; $acceptTerminalTitleChange(id: number, name: string): void; $acceptTerminalDimensions(id: number, cols: number, rows: number): void; $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 92d2e7ebf62..12f822bc5bb 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -395,17 +395,24 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I readonly onDidChangeTerminalDimensions = this._onDidChangeTerminalDimensions.event; protected readonly _onDidChangeTerminalState = new Emitter(); readonly onDidChangeTerminalState = this._onDidChangeTerminalState.event; - protected readonly _onDidWriteTerminalData: Emitter = new Emitter({ + protected readonly _onDidChangeShell = new Emitter(); + readonly onDidChangeShell = this._onDidChangeShell.event; + + protected readonly _onDidWriteTerminalData = new Emitter({ onWillAddFirstListener: () => this._proxy.$startSendingDataEvents(), onDidRemoveLastListener: () => this._proxy.$stopSendingDataEvents() }); readonly onDidWriteTerminalData = this._onDidWriteTerminalData.event; - protected readonly _onWillExecuteCommand = new Emitter(); + protected readonly _onWillExecuteCommand = new Emitter({ + onWillAddFirstListener: () => this._proxy.$startSendingCommandEvents(), + onDidRemoveLastListener: () => this._proxy.$stopSendingCommandEvents() + }); readonly onWillExecuteTerminalCommand = this._onWillExecuteCommand.event; - protected readonly _onDidExecuteCommand = new Emitter(); + protected readonly _onDidExecuteCommand = new Emitter({ + onWillAddFirstListener: () => this._proxy.$startSendingCommandEvents(), + onDidRemoveLastListener: () => this._proxy.$stopSendingCommandEvents() + }); readonly onDidExecuteTerminalCommand = this._onDidExecuteCommand.event; - protected readonly _onDidChangeShell = new Emitter(); - readonly onDidChangeShell = this._onDidChangeShell.event; constructor( supportsProcesses: boolean, @@ -514,6 +521,26 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I } } + public async $acceptWillExecuteCommand(id: number, command: any): Promise { + const terminal = this._getTerminalById(id); + if (terminal) { + this._onWillExecuteCommand.fire({ + terminal: terminal.value, + command: command + }); + } + } + + public async $acceptDidExecuteCommand(id: number, command: any): Promise { + const terminal = this._getTerminalById(id); + if (terminal) { + this._onDidExecuteCommand.fire({ + terminal: terminal.value, + command: command + }); + } + } + public async $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): Promise { // Extension pty terminal only - when virtual process resize fires it means that the // terminal's maximum dimensions changed diff --git a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts index bfd2e10a196..a9857b8e17b 100644 --- a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts @@ -9,7 +9,7 @@ declare module 'vscode' { export interface TerminalWillExecuteCommandEvent { terminal: Terminal; - command: TerminalCommand>; + command: TerminalCommand>; } export interface TerminalExecuteCommandEvent { From 49d8caf441662bc80913c4609ab4882ee31af418 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 24 Aug 2023 20:17:33 +0200 Subject: [PATCH 167/607] voice - log errors --- .../node/voiceRecognitionService.ts | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts index fdcd3ac332d..a63b58f6224 100644 --- a/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts +++ b/src/vs/platform/voiceRecognition/node/voiceRecognitionService.ts @@ -47,33 +47,41 @@ export class VoiceRecognitionService implements IVoiceRecognitionService { const now = Date.now(); - const voiceModule: { - transcribe: ( - audioBuffer: { channelCount: 1; samplingRate: 16000; bitDepth: 16; channelData: Float32Array }, - options: { - language: string | 'auto'; - suppressNonSpeechTokens: boolean; - signal: AbortSignal; - } - ) => Promise; - } = require.__$__nodeRequire(modulePath); + this.logService.info(`[voice] transcribe(${channelData.length}): Getting module from ${modulePath}`); - const abortController = new AbortController(); - cancellation.onCancellationRequested(() => abortController.abort()); + try { + const voiceModule: { + transcribe: ( + audioBuffer: { channelCount: 1; samplingRate: 16000; bitDepth: 16; channelData: Float32Array }, + options: { + language: string | 'auto'; + suppressNonSpeechTokens: boolean; + signal: AbortSignal; + } + ) => Promise; + } = require.__$__nodeRequire(modulePath); - const text = await voiceModule.transcribe({ - samplingRate: 16000, - bitDepth: 16, - channelCount: 1, - channelData - }, { - language: 'en', - suppressNonSpeechTokens: true, - signal: abortController.signal - }); + const abortController = new AbortController(); + cancellation.onCancellationRequested(() => abortController.abort()); - this.logService.info(`[voice] transcribe(${channelData.length}): End (text: "${text}", took: ${Date.now() - now}ms)`); + const text = await voiceModule.transcribe({ + samplingRate: 16000, + bitDepth: 16, + channelCount: 1, + channelData + }, { + language: 'en', + suppressNonSpeechTokens: true, + signal: abortController.signal + }); - return text; + this.logService.info(`[voice] transcribe(${channelData.length}): End (text: "${text}", took: ${Date.now() - now}ms)`); + + return text; + } catch (error) { + this.logService.error(`[voice] transcribe(${channelData.length}): Failed (error: "${error}", took: ${Date.now() - now}ms)`); + + throw error; + } } } From e8b6b1735f42df69d5a5bb4900295414213f4094 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 11:42:06 -0700 Subject: [PATCH 168/607] Get execute event working --- .../api/browser/mainThreadTerminalService.ts | 71 ++++++++++++++++--- .../workbench/api/common/extHost.protocol.ts | 13 +++- ....proposed.terminalExecuteCommandEvent.d.ts | 3 +- 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 4a7e2b8f2be..11a8e554fe8 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableStore, Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; -import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ITerminalDimensionsDto, ExtHostTerminalIdentifier, TerminalQuickFix } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, ExtHostTerminalServiceShape, MainThreadTerminalServiceShape, MainContext, TerminalLaunchConfig, ITerminalDimensionsDto, ExtHostTerminalIdentifier, TerminalQuickFix, ITerminalCommandDto } from 'vs/workbench/api/common/extHost.protocol'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { URI } from 'vs/base/common/uri'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -23,6 +23,7 @@ import { Promises } from 'vs/base/common/async'; import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import { ITerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { ITerminalQuickFixService, ITerminalQuickFix, TerminalQuickFixType } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; +import { ICommandDetectionCapability, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) @@ -228,18 +229,65 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._dataEventTracker.clear(); } + private _commandEventListeners = new MutableDisposable(); + public $startSendingCommandEvents(): void { - // TODO: Impl this._logService.info('$startSendingCommandEvents'); - setInterval(() => { - this._onWillExecuteCommand(this._terminalService.instances[0]!.instanceId, null); - this._onDidExecuteCommand(this._terminalService.instances[0]!.instanceId, null); - }, 3000); + if (this._commandEventListeners.value) { + return; + } + + // TODO: Add instance event multiplexer helper to service + const store = new DisposableStore(); + this._commandEventListeners.value = store; + for (const instance of this._terminalService.instances) { + // TODO: Track instance listeners in a map so disposed ones don't keep piling up? + store.add(this._initInstanceCommandListeners(instance)); + } + store.add(this._terminalService.onDidCreateInstance(instance => { + store.add(this._initInstanceCommandListeners(instance)); + })); + } + + private _initInstanceCommandListeners(instance: ITerminalInstance): IDisposable { + const store = new DisposableStore(); + const commandDetection = instance.capabilities.get(TerminalCapability.CommandDetection); + + const that = this; + function attachListener(commandDetection: ICommandDetectionCapability): IDisposable { + return commandDetection.onCommandFinished(e => { + that._onDidExecuteCommand(instance.instanceId, { + commandLine: e.command, + // TODO: Convert to URI if possible + cwd: e.cwd, + result: { + exitCode: e.exitCode, + output: e.getOutput() ?? '' + } + }); + }); + } + + if (commandDetection) { + store.add(attachListener(commandDetection)); + } + store.add(instance.capabilities.onDidAddCapability(capability => { + if (capability === TerminalCapability.CommandDetection) { + const commandDetection = instance.capabilities.get(TerminalCapability.CommandDetection); + if (commandDetection) { + store.add(attachListener(commandDetection)); + } + } + })); + + // TODO: Handle remove + + return store; } public $stopSendingCommandEvents(): void { this._logService.info('$stopSendingCommandEvents'); - // TODO: Impl + this._commandEventListeners.clear(); } public $startLinkProvider(): void { @@ -320,10 +368,11 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._proxy.$acceptTerminalProcessData(terminalId, data); } - private _onWillExecuteCommand(terminalId: number, command: any): void { - this._proxy.$acceptWillExecuteCommand(terminalId, command); - } - private _onDidExecuteCommand(terminalId: number, command: any): void { + // private _onWillExecuteCommand(terminalId: number, command: ITerminalCommandDto): void { + // this._proxy.$acceptWillExecuteCommand(terminalId, command); + // } + + private _onDidExecuteCommand(terminalId: number, command: ITerminalCommandDto): void { this._proxy.$acceptDidExecuteCommand(terminalId, command); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 1d575b97962..d1bc7c19b5d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -2091,14 +2091,23 @@ export interface TerminalCommandMatchResultDto { }; } +export interface ITerminalCommandDto { + commandLine: string; + cwd: URI | string | undefined; + result: { + exitCode: number | undefined; + output: string; + }; +} + export interface ExtHostTerminalServiceShape { $acceptTerminalClosed(id: number, exitCode: number | undefined, exitReason: TerminalExitReason): void; $acceptTerminalOpened(id: number, extHostTerminalId: string | undefined, name: string, shellLaunchConfig: IShellLaunchConfigDto): void; $acceptActiveTerminalChanged(id: number | null): void; $acceptTerminalProcessId(id: number, processId: number): void; $acceptTerminalProcessData(id: number, data: string): void; - $acceptWillExecuteCommand(id: number, command: any): void; - $acceptDidExecuteCommand(id: number, command: any): void; + $acceptWillExecuteCommand(id: number, command: ITerminalCommandDto): void; + $acceptDidExecuteCommand(id: number, command: ITerminalCommandDto): void; $acceptTerminalTitleChange(id: number, name: string): void; $acceptTerminalDimensions(id: number, cols: number, rows: number): void; $acceptTerminalMaximumDimensions(id: number, cols: number, rows: number): void; diff --git a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts index a9857b8e17b..6e51e6e7a05 100644 --- a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts @@ -44,7 +44,8 @@ declare module 'vscode' { exitCode: number; /** * The output of the command when it has finished executing. This is the plain text shown in - * the terminal buffer and does not include raw escape sequences.. + * the terminal buffer and does not include raw escape sequences. Depending on the shell + * setup, this may include the command line as part of the output. */ output: string; } From 7a860fb4f5288dabd2b2dc88570a0800a963b322 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 24 Aug 2023 11:43:01 -0700 Subject: [PATCH 169/607] Fix tests --- .../test/browser/notebookKernelHistory.test.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts index bb7e0967612..52eadf224ae 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelHistory.test.ts @@ -18,7 +18,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { NotebookKernelHistoryService } from 'vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl'; -import { IStorageService, IWillSaveStateEvent, StorageScope } from 'vs/platform/storage/common/storage'; +import { IApplicationStorageValueChangeEvent, IProfileStorageValueChangeEvent, IStorageService, IStorageValueChangeEvent, IWillSaveStateEvent, IWorkspaceStorageValueChangeEvent, StorageScope } from 'vs/platform/storage/common/storage'; import { INotebookLoggingService } from 'vs/workbench/contrib/notebook/common/notebookLoggingService'; suite('NotebookKernelHistoryService', () => { @@ -70,6 +70,12 @@ suite('NotebookKernelHistoryService', () => { instantiationService.stub(IStorageService, new class extends mock() { override onWillSaveState: Event = Event.None; + override onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event; + override onDidChangeValue(scope: StorageScope.PROFILE, key: string | undefined, disposable: DisposableStore): Event; + override onDidChangeValue(scope: StorageScope.APPLICATION, key: string | undefined, disposable: DisposableStore): Event; + override onDidChangeValue(scope: StorageScope, key: string | undefined, disposable: DisposableStore): Event { + return Event.None; + } override get(key: string, scope: StorageScope, fallbackValue: string): string; override get(key: string, scope: StorageScope, fallbackValue?: string | undefined): string | undefined; override get(key: unknown, scope: unknown, fallbackValue?: unknown): string | undefined { @@ -119,6 +125,12 @@ suite('NotebookKernelHistoryService', () => { instantiationService.stub(IStorageService, new class extends mock() { override onWillSaveState: Event = Event.None; + override onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event; + override onDidChangeValue(scope: StorageScope.PROFILE, key: string | undefined, disposable: DisposableStore): Event; + override onDidChangeValue(scope: StorageScope.APPLICATION, key: string | undefined, disposable: DisposableStore): Event; + override onDidChangeValue(scope: StorageScope, key: string | undefined, disposable: DisposableStore): Event { + return Event.None; + } override get(key: string, scope: StorageScope, fallbackValue: string): string; override get(key: string, scope: StorageScope, fallbackValue?: string | undefined): string | undefined; override get(key: unknown, scope: unknown, fallbackValue?: unknown): string | undefined { From 794400449fb70a6ff317410152d5997fdac35c35 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 11:55:43 -0700 Subject: [PATCH 170/607] fix #191238 --- .../contrib/accessibility/browser/accessibleView.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 33deac70738..506af0bfe15 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -187,6 +187,7 @@ class AccessibleView extends Disposable { if (!showAccessibleViewHelp) { this._currentProvider = undefined; this._accessibleViewCurrentProviderId.reset(); + this._updateContextKeys(provider!, false); } } }; @@ -267,6 +268,7 @@ class AccessibleView extends Disposable { this._editorWidget.revealLine(index + 1); this._editorWidget.setSelection({ startLineNumber: index + 1, startColumn: 1, endLineNumber: index + 1, endColumn: 1 }); } + this._updateContextKeys(provider, true); } disableHint(): void { @@ -280,10 +282,10 @@ class AccessibleView extends Disposable { private _updateContextKeys(provider: IAccessibleContentProvider, shown: boolean): void { if (provider.options.type === AccessibleViewType.Help) { this._accessiblityHelpIsShown.set(shown); - this._accessibleViewIsShown.set(!shown); + this._accessibleViewIsShown.reset(); } else { this._accessibleViewIsShown.set(shown); - this._accessiblityHelpIsShown.set(!shown); + this._accessiblityHelpIsShown.reset(); } if (provider.next && provider.previous) { this._accessibleViewSupportsNavigation.set(true); From 160947d5922cf50d842d6caa0e14a75d10316088 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 12:26:37 -0700 Subject: [PATCH 171/607] Add terminal event multiplexers, hook up execute command --- .../common/capabilities/capabilities.ts | 17 +++++ .../capabilities/terminalCapabilityStore.ts | 20 ++++- .../api/browser/mainThreadTerminalService.ts | 64 ++++------------ .../contrib/terminal/browser/terminal.ts | 16 +++- .../terminal/browser/terminalService.ts | 75 ++++++++++++++++++- 5 files changed, 139 insertions(+), 53 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index e7b7dcb57bd..c8fd3e5f85a 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -84,14 +84,26 @@ export interface ITerminalCapabilityStore { /** * Fired when a capability is added. + * @deprecated Use onDidAddCapability2 */ readonly onDidAddCapability: Event; /** * Fired when a capability is removed. + * @deprecated Use onDidRemoveCapability2 */ readonly onDidRemoveCapability: Event; + /** + * Fired when a capability is added. + */ + readonly onDidAddCapability2: Event>; + + /** + * Fired when a capability is removed. + */ + readonly onDidRemoveCapability2: Event>; + /** * Gets whether the capability exists in the store. */ @@ -103,6 +115,11 @@ export interface ITerminalCapabilityStore { get(capability: T): ITerminalCapabilityImplMap[T] | undefined; } +export interface TerminalCapabilityChangeEvent { + id: T; + capability: ITerminalCapabilityImplMap[T]; +} + /** * Maps capability types to their implementation, enabling strongly typed fetching of * implementations. diff --git a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts index 69d51539258..bdc3b4fdb50 100644 --- a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts +++ b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts @@ -5,7 +5,7 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ITerminalCapabilityImplMap, ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { ITerminalCapabilityImplMap, ITerminalCapabilityStore, TerminalCapability, TerminalCapabilityChangeEvent } from 'vs/platform/terminal/common/capabilities/capabilities'; export class TerminalCapabilityStore extends Disposable implements ITerminalCapabilityStore { private _map: Map = new Map(); @@ -15,6 +15,11 @@ export class TerminalCapabilityStore extends Disposable implements ITerminalCapa private readonly _onDidAddCapability = this._register(new Emitter()); readonly onDidAddCapability = this._onDidAddCapability.event; + private readonly _onDidRemoveCapability2 = this._register(new Emitter>()); + readonly onDidRemoveCapability2 = this._onDidRemoveCapability2.event; + private readonly _onDidAddCapability2 = this._register(new Emitter>()); + readonly onDidAddCapability2 = this._onDidAddCapability2.event; + get items(): IterableIterator { return this._map.keys(); } @@ -22,6 +27,7 @@ export class TerminalCapabilityStore extends Disposable implements ITerminalCapa add(capability: T, impl: ITerminalCapabilityImplMap[T]) { this._map.set(capability, impl); this._onDidAddCapability.fire(capability); + this._onDidAddCapability2.fire({ id: capability, capability: impl }); } get(capability: T): ITerminalCapabilityImplMap[T] | undefined { @@ -30,11 +36,13 @@ export class TerminalCapabilityStore extends Disposable implements ITerminalCapa } remove(capability: TerminalCapability) { - if (!this._map.has(capability)) { + const impl = this._map.get(capability); + if (!impl) { return; } this._map.delete(capability); this._onDidRemoveCapability.fire(capability); + this._onDidAddCapability2.fire({ id: capability, capability: impl }); } has(capability: TerminalCapability) { @@ -50,6 +58,11 @@ export class TerminalCapabilityStoreMultiplexer extends Disposable implements IT private readonly _onDidAddCapability = this._register(new Emitter()); readonly onDidAddCapability = this._onDidAddCapability.event; + private readonly _onDidRemoveCapability2 = this._register(new Emitter>()); + readonly onDidRemoveCapability2 = this._onDidRemoveCapability2.event; + private readonly _onDidAddCapability2 = this._register(new Emitter>()); + readonly onDidAddCapability2 = this._onDidAddCapability2.event; + get items(): IterableIterator { return this._items(); } @@ -87,8 +100,11 @@ export class TerminalCapabilityStoreMultiplexer extends Disposable implements IT this._stores.push(store); for (const capability of store.items) { this._onDidAddCapability.fire(capability); + this._onDidAddCapability2.fire({ id: capability, capability: store.get(capability)! }); } store.onDidAddCapability(e => this._onDidAddCapability.fire(e)); + store.onDidAddCapability2(e => this._onDidAddCapability2.fire(e)); store.onDidRemoveCapability(e => this._onDidRemoveCapability.fire(e)); + store.onDidRemoveCapability2(e => this._onDidRemoveCapability2.fire(e)); } } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index 11a8e554fe8..a4247e13a3f 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -23,7 +23,7 @@ import { Promises } from 'vs/base/common/async'; import { ISerializableEnvironmentDescriptionMap, ISerializableEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import { ITerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; import { ITerminalQuickFixService, ITerminalQuickFix, TerminalQuickFixType } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; -import { ICommandDetectionCapability, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; @extHostNamedCustomer(MainContext.MainThreadTerminalService) @@ -229,65 +229,33 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._dataEventTracker.clear(); } - private _commandEventListeners = new MutableDisposable(); + private _sendCommandEventListener = new MutableDisposable(); public $startSendingCommandEvents(): void { this._logService.info('$startSendingCommandEvents'); - if (this._commandEventListeners.value) { + if (this._sendCommandEventListener.value) { return; } - // TODO: Add instance event multiplexer helper to service - const store = new DisposableStore(); - this._commandEventListeners.value = store; - for (const instance of this._terminalService.instances) { - // TODO: Track instance listeners in a map so disposed ones don't keep piling up? - store.add(this._initInstanceCommandListeners(instance)); - } - store.add(this._terminalService.onDidCreateInstance(instance => { - store.add(this._initInstanceCommandListeners(instance)); - })); - } + const multiplexer = this._terminalService.createInstanceCapabilityEventMultiplexer(TerminalCapability.CommandDetection, capability => capability.onCommandFinished); + this._sendCommandEventListener.value = multiplexer; - private _initInstanceCommandListeners(instance: ITerminalInstance): IDisposable { - const store = new DisposableStore(); - const commandDetection = instance.capabilities.get(TerminalCapability.CommandDetection); - - const that = this; - function attachListener(commandDetection: ICommandDetectionCapability): IDisposable { - return commandDetection.onCommandFinished(e => { - that._onDidExecuteCommand(instance.instanceId, { - commandLine: e.command, - // TODO: Convert to URI if possible - cwd: e.cwd, - result: { - exitCode: e.exitCode, - output: e.getOutput() ?? '' - } - }); - }); - } - - if (commandDetection) { - store.add(attachListener(commandDetection)); - } - store.add(instance.capabilities.onDidAddCapability(capability => { - if (capability === TerminalCapability.CommandDetection) { - const commandDetection = instance.capabilities.get(TerminalCapability.CommandDetection); - if (commandDetection) { - store.add(attachListener(commandDetection)); + multiplexer.event(e => { + this._onDidExecuteCommand(e.instance.instanceId, { + commandLine: e.data.command, + // TODO: Convert to URI if possible + cwd: e.data.cwd, + result: { + exitCode: e.data.exitCode, + output: e.data.getOutput() ?? '' } - } - })); - - // TODO: Handle remove - - return store; + }); + }); } public $stopSendingCommandEvents(): void { this._logService.info('$stopSendingCommandEvents'); - this._commandEventListeners.clear(); + this._sendCommandEventListener.clear(); } public $startLinkProvider(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 61b92bd54d9..d418b94263f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -12,7 +12,7 @@ import { OperatingSystem } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IKeyMods } from 'vs/platform/quickinput/common/quickInput'; -import { IMarkProperties, ITerminalCapabilityStore, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { IMarkProperties, ITerminalCapabilityImplMap, ITerminalCapabilityStore, ITerminalCommand, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { IMergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariable'; import { IExtensionTerminalProfile, IReconnectionProperties, IShellIntegration, IShellLaunchConfig, ITerminalBackend, ITerminalDimensions, ITerminalLaunchError, ITerminalProfile, ITerminalTabLayoutInfoById, TerminalExitReason, TerminalIcon, TerminalLocation, TerminalShellType, TerminalType, TitleEventSource, WaitOnExitValue } from 'vs/platform/terminal/common/terminal'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; @@ -263,6 +263,20 @@ export interface ITerminalService extends ITerminalInstanceHost { getEditingTerminal(): ITerminalInstance | undefined; setEditingTerminal(instance: ITerminalInstance | undefined): void; + + /** + * Creates an instance event listener that listens to all instances, dynamically adding new + * instances and removing old instances as needed. + * @param getEvent Maps the instance to the event. + */ + createInstanceEventMultiplexer(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event }; + + /** + * Creates a capability event listener that listens to capabilities on all instances, + * dynamically adding and removing instances and capabilities as needed. + * @param getEvent Maps the capability to the event. + */ + createInstanceCapabilityEventMultiplexer(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> }; } export class TerminalLinkQuickPickEvent extends MouseEvent { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 2423c3958b1..e0422c9e24f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -6,8 +6,8 @@ import * as dom from 'vs/base/browser/dom'; import { DeferredPromise, timeout } from 'vs/base/common/async'; import { debounce } from 'vs/base/common/decorators'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Emitter, Event, EventMultiplexer } from 'vs/base/common/event'; +import { Disposable, DisposableMap, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -53,6 +53,7 @@ import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilitie import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; import { mark } from 'vs/base/common/performance'; import { DeatachedTerminal } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; +import { ITerminalCapabilityImplMap, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; export class TerminalService extends Disposable implements ITerminalService { declare _serviceBrand: undefined; @@ -1194,6 +1195,76 @@ export class TerminalService extends Disposable implements ITerminalService { setEditingTerminal(instance: ITerminalInstance | undefined) { this._editingTerminal = instance; } + + createInstanceEventMultiplexer(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event } { + const store = new DisposableStore(); + const multiplexer = store.add(new EventMultiplexer()); + const instanceListeners = store.add(new DisposableMap()); + + function addInstance(instance: ITerminalInstance) { + const listener = multiplexer.add(getEvent(instance)); + instanceListeners.set(instance, listener); + } + + // Existing instances + for (const instance of this.instances) { + addInstance(instance); + } + + // Added instances + store.add(this.onDidCreateInstance(instance => { + addInstance(instance); + })); + + // Removed instances + store.add(this.onDidDisposeInstance(instance => { + instanceListeners.deleteAndDispose(instance); + })); + + return { + dispose: () => store.dispose(), + event: multiplexer.event + }; + } + + createInstanceCapabilityEventMultiplexer(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { + const store = new DisposableStore(); + const multiplexer = store.add(new EventMultiplexer<{ instance: ITerminalInstance; data: K }>()); + const capabilityListeners = store.add(new DisposableMap()); + + function addCapability(instance: ITerminalInstance, capability: ITerminalCapabilityImplMap[T]) { + const listener = multiplexer.add(Event.map(getEvent(capability), data => ({ instance, data }))); + capabilityListeners.set(capability, listener); + } + + // Existing instances + for (const instance of this.instances) { + const capability = instance.capabilities.get(capabilityId); + if (capability) { + addCapability(instance, capability); + } + } + + // TODO: Verify this is valid; the capabilities need to be added after onDidCreateInstance + const addCapabilityMultiplexer = this.createInstanceEventMultiplexer(instance => Event.map(instance.capabilities.onDidAddCapability2, changeEvent => ({ instance, changeEvent }))); + addCapabilityMultiplexer.event(e => { + if (e.changeEvent.id === capabilityId) { + addCapability(e.instance, e.changeEvent.capability); + } + }); + + const removeCapabilityMultiplexer = this.createInstanceEventMultiplexer(instance => instance.capabilities.onDidRemoveCapability2); + removeCapabilityMultiplexer.event(e => { + if (e.id === capabilityId) { + capabilityListeners.deleteAndDispose(e.capability); + } + }); + + return { + dispose: () => store.dispose(), + event: multiplexer.event + }; + } } class TerminalEditorStyle extends Themable { From c1383444012a7de8f2aab670be919d478f4d202d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 12:37:36 -0700 Subject: [PATCH 172/607] Document EventMultiplexer --- src/vs/base/common/event.ts | 23 +++++++++++++++++++ .../terminal/browser/terminalService.ts | 5 ++-- ....proposed.terminalExecuteCommandEvent.d.ts | 3 +++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 872263f2e9e..ae7f8701dca 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -1343,6 +1343,29 @@ export class MicrotaskEmitter extends Emitter { } } +/** + * An event emitter that multiplexes many events into a single event. + * + * @example Listen to the `onData` event of all `Thing`s, dynamically adding and removing `Thing`s + * to the multiplexer as needed. + * + * ```typescript + * const anythingDataMultiplexer = new EventMultiplexer<{ data: string }>(); + * + * const thingListeners = DisposableMap(); + * + * thingService.onDidAddThing(thing => { + * thingListeners.set(thing, anythingDataMultiplexer.add(thing.onData); + * }); + * thingService.onDidRemoveThing(thing => { + * thingListeners.deleteAndDispose(thing); + * }); + * + * anythingDataMultiplexer.event(e => { + * console.log('Something fired data ' + e.data) + * }); + * ``` + */ export class EventMultiplexer implements IDisposable { private readonly emitter: Emitter; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index e0422c9e24f..b3bb2d1d2cb 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -1237,7 +1237,7 @@ export class TerminalService extends Disposable implements ITerminalService { capabilityListeners.set(capability, listener); } - // Existing instances + // Existing capabilities for (const instance of this.instances) { const capability = instance.capabilities.get(capabilityId); if (capability) { @@ -1245,7 +1245,7 @@ export class TerminalService extends Disposable implements ITerminalService { } } - // TODO: Verify this is valid; the capabilities need to be added after onDidCreateInstance + // Added capabilities const addCapabilityMultiplexer = this.createInstanceEventMultiplexer(instance => Event.map(instance.capabilities.onDidAddCapability2, changeEvent => ({ instance, changeEvent }))); addCapabilityMultiplexer.event(e => { if (e.changeEvent.id === capabilityId) { @@ -1253,6 +1253,7 @@ export class TerminalService extends Disposable implements ITerminalService { } }); + // Removed capabilities const removeCapabilityMultiplexer = this.createInstanceEventMultiplexer(instance => instance.capabilities.onDidRemoveCapability2); removeCapabilityMultiplexer.event(e => { if (e.id === capabilityId) { diff --git a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts index 6e51e6e7a05..09e8146a10d 100644 --- a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts @@ -59,6 +59,9 @@ declare module 'vscode' { /** * An event that is emitted when a terminal with shell integration activated has completed * executing a command. + * + * Note that this event will not fire if the executed command exits the shell, listen to + * {@link onDidOpenTerminal} to handle that case. */ export const onDidExecuteTerminalCommand: Event; } From ecb0c80fc1da718cabb863409897d941bf18aa49 Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Thu, 24 Aug 2023 12:53:41 -0700 Subject: [PATCH 173/607] Bump extension telemetry module (#191237) * Bump extension telemetry module * Fix webpack --- extensions/git/package.json | 2 +- extensions/git/yarn.lock | 393 ++++++++++++----- extensions/github-authentication/package.json | 2 +- extensions/github-authentication/yarn.lock | 393 ++++++++++++----- extensions/github/package.json | 2 +- extensions/github/yarn.lock | 398 +++++++++++++----- .../markdown-language-features/package.json | 2 +- .../markdown-language-features/yarn.lock | 376 ++++++++++++----- extensions/media-preview/package.json | 2 +- extensions/media-preview/yarn.lock | 393 ++++++++++++----- extensions/merge-conflict/package.json | 2 +- extensions/merge-conflict/yarn.lock | 393 ++++++++++++----- .../microsoft-authentication/package.json | 2 +- extensions/microsoft-authentication/yarn.lock | 393 ++++++++++++----- extensions/shared.webpack.config.js | 2 + extensions/simple-browser/package.json | 2 +- extensions/simple-browser/yarn.lock | 393 ++++++++++++----- .../typescript-language-features/package.json | 2 +- .../typescript-language-features/yarn.lock | 381 ++++++++++++----- 19 files changed, 2635 insertions(+), 898 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index d7d241e0aab..105491916ca 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3005,7 +3005,7 @@ }, "dependencies": { "@joaomoreno/unique-names-generator": "^5.1.0", - "@vscode/extension-telemetry": "0.7.5", + "@vscode/extension-telemetry": "^0.8.4", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 6be6a6b4e43..bb3a09d947c 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,71 +80,105 @@ dependencies: tslib "^2.2.0" +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== + dependencies: + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + "@joaomoreno/unique-names-generator@^5.1.0": version "5.1.0" resolved "https://registry.yarnpkg.com/@joaomoreno/unique-names-generator/-/unique-names-generator-5.1.0.tgz#d577d425aed794c44c0e8863cddd5dea349f74f3" integrity sha512-KEVThTpUIKPb7dBKJ9mJ3WYnD1mJZZsEinCSp9CVEPlWbDagurFv1RKRjvvujrLfJzsGc0HkBHS9W8Bughao4A== -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -131,39 +190,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tokenizer/token@^0.3.0": version "0.3.0" @@ -202,26 +296,41 @@ resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + "@types/which@3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@types/which/-/which-3.0.0.tgz#849afdd9fdcb0b67339b9cfc80fa6ea4e0253fc5" integrity sha512-ASCxdbsrwNfSMXALlC3Decif9rwDMu+80KGp5zI2RLRotfMsTv7fHL8W8VDp24wymzDyIFudhUeSCugrgRFfHQ== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" "@vscode/iconv-lite-umd@0.7.0": version "0.7.0" resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -229,22 +338,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -271,6 +382,11 @@ byline@^5.0.0: resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -295,7 +411,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -307,17 +423,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -344,6 +460,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -366,11 +494,28 @@ ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -381,6 +526,13 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -393,11 +545,21 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + peek-readable@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-4.1.0.tgz#4ece1111bf5c2ad8867c314c81356847e8a62e72" @@ -424,6 +586,24 @@ readable-web-to-node-stream@^3.0.0: dependencies: readable-stream "^3.6.0" +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -434,7 +614,14 @@ semver@^5.3.0, semver@^5.4.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -459,6 +646,11 @@ strtok3@^6.2.4: "@tokenizer/token" "^0.3.0" peek-readable "^4.1.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + token-types@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/token-types/-/token-types-4.2.0.tgz#b66bc3d67420c6873222a424eee64a744f4c2f13" @@ -493,3 +685,8 @@ which@3.0.1: integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== dependencies: isexe "^2.0.0" + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index f5e3c95e6dd..4855716e08e 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -60,7 +60,7 @@ }, "dependencies": { "node-fetch": "2.6.7", - "@vscode/extension-telemetry": "0.7.5", + "@vscode/extension-telemetry": "^0.8.4", "vscode-tas-client": "^0.1.47" }, "devDependencies": { diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 858f60ddaff..da5f5631576 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,39 +185,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -183,15 +277,30 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -200,22 +309,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -244,6 +355,11 @@ axios@^0.26.1: dependencies: follow-redirects "^1.14.8" +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -268,7 +384,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -280,17 +396,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -322,6 +438,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -339,6 +467,30 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" @@ -351,6 +503,11 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -363,12 +520,42 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -378,6 +565,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tas-client@0.1.45: version "0.1.45" resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.45.tgz#83bbf73f8458a0f527f9a389f7e1c37f63a64a76" @@ -419,3 +611,8 @@ whatwg-url@^5.0.0: dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/github/package.json b/extensions/github/package.json index ec02df2ec85..afbb7253f7f 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -183,7 +183,7 @@ "@octokit/graphql-schema": "14.4.0", "@octokit/rest": "19.0.4", "tunnel": "^0.0.6", - "@vscode/extension-telemetry": "0.7.5" + "@vscode/extension-telemetry": "^0.8.4" }, "devDependencies": { "@types/node": "18.x" diff --git a/extensions/github/yarn.lock b/extensions/github/yarn.lock index f562d36c972..ab4275a38b9 100644 --- a/extensions/github/yarn.lock +++ b/extensions/github/yarn.lock @@ -17,32 +17,50 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": - version "1.11.0" - resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.11.0.tgz#fc0e8f56caac08a9d4ac91c07a6c5a360ea31c82" - integrity sha512-nB4KXl6qAyJmBVLWA7SakT4tzpYZTCk4pvRBeI+Ye0WYSOrlTqlMhc4MSS/8atD3ufeYWdkN380LLoXlUUzThw== +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" + integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== dependencies: "@azure/abort-controller" "^1.0.0" "@azure/core-auth" "^1.4.0" "@azure/core-tracing" "^1.0.1" - "@azure/core-util" "^1.3.0" + "@azure/core-util" "^1.0.0" "@azure/logger" "^1.0.0" form-data "^4.0.0" http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" tslib "^2.2.0" + uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" -"@azure/core-util@^1.3.0": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.3.2.tgz#3f8cfda1e87fac0ce84f8c1a42fcd6d2a986632d" - integrity sha512-2bECOUh88RvL1pMZTcc6OzfobBeWDBf5oBbhjIhT1MV9otMVWCzpOJkkiKtrnO88y5GGBelgY8At73KGAdbkeQ== +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + +"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== dependencies: "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" @@ -54,66 +72,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.12", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.12" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.12.tgz#f5f56626bd0385a357fae6f730eea347be02ce64" - integrity sha512-cHpxZZ+pbtOyqFMFB/c1COpaOE3VPFU6phYVHVvOA9DvoeMZfI/Xrxaj7B/vfq4MmkiE7nOAPhv5ZRn+i6OogA== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.14" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.12" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.12.tgz#60f6ff48ba48c88880c1bceb376711cdd34f87ea" - integrity sha512-vhIVYg4FzBfwtM8tBqDUq3xU+cFu6SQ7biuJHtQpd5PVjDgvAovVOMRF1khsZE/k2rttRRBpmBgNEqG3Ptoysw== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.12" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.14": - version "2.8.14" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.14.tgz#daabd8a418d9b70a318c0126518e000dd6f67fa0" - integrity sha512-z1AG6lqV3ACtdUXnT0Ubj48BAZ8K01sFsYdWgroSXpw2lYUlXAzdx3tK8zpaqEXSEhok8CWTZki7aunHzkZHSw== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== + dependencies: + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" + +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== + dependencies: + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" + +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: - "@microsoft/applicationinsights-common" "2.8.14" - "@microsoft/applicationinsights-core-js" "2.8.14" "@microsoft/applicationinsights-shims" "2.0.2" "@microsoft/dynamicproto-js" "^1.1.9" -"@microsoft/applicationinsights-common@2.8.14": - version "2.8.14" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.14.tgz#7d082295f862a189c80aa98b3f4aaec926546051" - integrity sha512-1xjJvyyRN7tb5ahOTkEGGsvw8zvqmS714y3+1m7ooKHFfxO0wX+eYOU/kke74BCY0nJ/pocB/6hjWZOgwvbHig== +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.14" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.9" - -"@microsoft/applicationinsights-core-js@2.8.14": - version "2.8.14" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.14.tgz#80e3d9d42102e741494726d78ac923098bad7132" - integrity sha512-XacWUHdjSHMUwdngMZBp0oiCBifD56CQK2Egu2PiBiF4xu2AO2yNCtWSXsQX2g5OkEhVwaEjfa/aH3WbpYxB1g== - dependencies: - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.9" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.14" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.14.tgz#8c43bcad2e12f25eb00a9aaad0182371507b21b9" - integrity sha512-R2mzg5NmCtLloq3lPQFmnlvjrPIqm3mWNYVy5ELJuOPZ7S6j9y7s4yHOzfXynmOziiQd+0q1j9pTth9aP9vo0g== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.14" - "@microsoft/applicationinsights-common" "2.8.14" - "@microsoft/applicationinsights-core-js" "2.8.14" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.9" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -125,6 +177,25 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== + dependencies: + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== + dependencies: + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" + +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + "@octokit/auth-token@^3.0.0": version "3.0.1" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.1.tgz#88bc2baf5d706cb258474e722a720a8365dff2ec" @@ -255,39 +326,50 @@ dependencies: "@octokit/openapi-types" "^17.1.0" -"@opentelemetry/api@^1.0.4": +"@opentelemetry/api@^1.4.1": version "1.4.1" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== -"@opentelemetry/core@1.14.0", "@opentelemetry/core@^1.0.1": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.14.0.tgz#64e876b29cb736c984d54164cd47433f513eafd3" - integrity sha512-MnMZ+sxsnlzloeuXL2nm5QcNczt/iO82UOeQQDHhV83F2fP3sgntW2evvtoxJki0MBLxEsh5ADD7PR/Hn5uzjw== +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/semantic-conventions" "1.14.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/resources@1.14.0": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.14.0.tgz#d6b0a4e71c2706d33c8c6ec7a7b8fea6ad27ddea" - integrity sha512-qRfWIgBxxl3z47E036Aey0Lj2ZjlFb27Q7Xnj1y1z/P293RXJZGLtcfn/w8JF7v1Q2hs3SDGxz7Wb9Dko1YUQA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== dependencies: - "@opentelemetry/core" "1.14.0" - "@opentelemetry/semantic-conventions" "1.14.0" + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.14.0.tgz#831af08f002228a11e577ff860eb6059c8b80fb7" - integrity sha512-NzRGt3PS+HPKfQYMb6Iy8YYc5OKA73qDwci/6ujOIvyW9vcqBJSWbjZ8FeLEAmuatUB5WrRhEKu9b0sIiIYTrQ== +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== dependencies: - "@opentelemetry/core" "1.14.0" - "@opentelemetry/resources" "1.14.0" - "@opentelemetry/semantic-conventions" "1.14.0" + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.14.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.14.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.14.0.tgz#6a729b7f372ce30f77a3f217c09bc216f863fccb" - integrity sha512-rJfCY8rCWz3cb4KI6pEofnytvMPuj3YLQwoscCCYZ5DkdiPjo15IQ0US7+mjcWy9H3fcZIzf2pbJZ7ck/h4tug== +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -299,15 +381,30 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -316,22 +413,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -358,6 +457,11 @@ before-after-hook@^2.2.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -382,7 +486,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -399,17 +503,17 @@ deprecation@^2.0.0, deprecation@^2.3.1: resolved "https://registry.yarnpkg.com/deprecation/-/deprecation-2.3.1.tgz#6368cbdb40abf3373b525ac87e4a260c3a700919" integrity sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -427,6 +531,11 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + graphql-tag@^2.10.3: version "2.12.6" resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1" @@ -439,6 +548,13 @@ graphql@^16.0.0: resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.6.0.tgz#c2dcffa4649db149f6282af726c8c83f1c7c5fdb" integrity sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw== +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -456,11 +572,35 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -473,6 +613,11 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -492,12 +637,42 @@ once@^1.4.0: dependencies: wrappy "1" +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -507,6 +682,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -532,6 +712,11 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== +uuid@^8.3.0: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -549,3 +734,8 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 78c71ce9138..9743845b8d9 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -726,7 +726,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "0.7.5", + "@vscode/extension-telemetry": "^0.8.4", "dompurify": "^3.0.5", "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 8a47f870e93..36b5aebfed8 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,39 +185,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -207,6 +301,11 @@ resolved "https://registry.yarnpkg.com/@types/picomatch/-/picomatch-2.3.0.tgz#75db5e75a713c5a83d5b76780c3da84a82806003" integrity sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + "@types/trusted-types@*": version "2.0.2" resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756" @@ -222,21 +321,31 @@ resolved "https://registry.yarnpkg.com/@types/vscode-webview/-/vscode-webview-1.57.0.tgz#bad5194d45ae8d03afc1c0f67f71ff5e7a243bbf" integrity sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" "@vscode/l10n@^0.0.10": version "0.0.10" resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.10.tgz#9c513107c690c0dd16e3ec61e453743de15ebdb0" integrity sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ== +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -244,22 +353,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" argparse@^2.0.1: version "2.0.1" @@ -299,6 +410,11 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -328,7 +444,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -340,17 +456,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" dompurify@^3.0.5: version "3.0.5" @@ -378,6 +494,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + highlight.js@^11.8.0: version "11.8.0" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-11.8.0.tgz#966518ea83257bae2e7c9a48596231856555bb65" @@ -400,6 +528,23 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + linkify-it@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-3.0.3.tgz#a98baf44ce45a550efb4d49c769d07524cc2fa2e" @@ -459,6 +604,11 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + morphdom@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/morphdom/-/morphdom-2.6.1.tgz#e868e24f989fa3183004b159aed643e628b4306e" @@ -469,24 +619,47 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^7.3.5: +semver@^7.3.5, semver@^7.5.1, semver@^7.5.3: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -shimmer@^1.1.0, shimmer@^1.2.0: +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -496,6 +669,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tslib@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" diff --git a/extensions/media-preview/package.json b/extensions/media-preview/package.json index 3107d9d60bf..6c1b46220b9 100644 --- a/extensions/media-preview/package.json +++ b/extensions/media-preview/package.json @@ -126,7 +126,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "0.7.5", + "@vscode/extension-telemetry": "^0.8.4", "vscode-uri": "^3.0.6" }, "repository": { diff --git a/extensions/media-preview/yarn.lock b/extensions/media-preview/yarn.lock index 971c4c2c50a..36b652d7d7b 100644 --- a/extensions/media-preview/yarn.lock +++ b/extensions/media-preview/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,54 +185,104 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -182,22 +291,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -219,6 +330,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -243,7 +359,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -255,17 +371,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -283,6 +399,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -300,6 +428,30 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -312,17 +464,52 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -332,6 +519,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tslib@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -346,3 +538,8 @@ vscode-uri@^3.0.6: version "3.0.6" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.6.tgz#5e6e2e1a4170543af30151b561a41f71db1d6f91" integrity sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index 00a7136cff2..751e85c3da2 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -166,7 +166,7 @@ } }, "dependencies": { - "@vscode/extension-telemetry": "0.7.5" + "@vscode/extension-telemetry": "^0.8.4" }, "devDependencies": { "@types/node": "18.x" diff --git a/extensions/merge-conflict/yarn.lock b/extensions/merge-conflict/yarn.lock index f07c9a13701..d214c5e4447 100644 --- a/extensions/merge-conflict/yarn.lock +++ b/extensions/merge-conflict/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,39 +185,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.3.0.tgz#27c6f776ac3c1c616651e506a89f438a0ed6a055" - integrity sha512-YveTnGNsFFixTKJz09Oi4zYkiLT5af3WpZDu4aIUM7xX+2bHAkOJayFTVQd6zB8kkWPpbua4Ha6Ql00grdLlJQ== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.8.0", "@opentelemetry/core@^1.0.1": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.8.0.tgz#cca18594dd48ded6dc0d08c7e789c79af0315934" - integrity sha512-6SDjwBML4Am0AQmy7z1j6HGrWDgeK8awBRUvl1PGw6HayViMk4QpnUXvv4HTHisecgVBy43NE/cstWprm8tIfw== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.8.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.8.0": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.8.0.tgz#260be9742cf7bceccc0db928d8ca8d64391acfe3" - integrity sha512-KSyMH6Jvss/PFDy16z5qkCK0ERlpyqixb1xwb73wLMvVq+j7i89lobDjw3JkpCcd1Ws0J6jAI4fw28Zufj2ssg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.8.0" - "@opentelemetry/semantic-conventions" "1.8.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.8.0.tgz#70713aab90978a16dea188c8335209f857be7384" - integrity sha512-iH41m0UTddnCKJzZx3M85vlhKzRcmT48pUeBbnzsGrq4nIay1oWVHKM5nhB5r8qRDGvd/n7f/YLCXClxwM0tvA== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.8.0" - "@opentelemetry/resources" "1.8.0" - "@opentelemetry/semantic-conventions" "1.8.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.8.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.8.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.8.0.tgz#fe2aa90e6df050a11cd57f5c0f47b0641fd2cad3" - integrity sha512-TYh1MRcm4JnvpqtqOwT9WYaBYY4KERHdToxs/suDTLviGRsQkIjS5yYROTYTSJQUnYLOn/TuOh5GoMwfLSU+Ew== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -170,15 +264,30 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -187,22 +296,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -224,6 +335,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -248,7 +364,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -260,17 +376,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -288,6 +404,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -305,6 +433,30 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -317,17 +469,52 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -337,6 +524,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tslib@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -346,3 +538,8 @@ uuid@^8.3.0: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index c0ed977db77..8747c4a1df8 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -118,7 +118,7 @@ "dependencies": { "node-fetch": "2.6.7", "@azure/ms-rest-azure-env": "^2.0.0", - "@vscode/extension-telemetry": "0.7.5" + "@vscode/extension-telemetry": "^0.8.4" }, "repository": { "type": "git", diff --git a/extensions/microsoft-authentication/yarn.lock b/extensions/microsoft-authentication/yarn.lock index 13322694966..68b4aa00b84 100644 --- a/extensions/microsoft-authentication/yarn.lock +++ b/extensions/microsoft-authentication/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -60,66 +85,100 @@ resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz#45809f89763a480924e21d3c620cd40866771625" integrity sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw== -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -131,39 +190,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -202,20 +296,35 @@ dependencies: "@types/node" "*" +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + "@types/uuid@8.0.0": version "8.0.0" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -224,22 +333,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -261,6 +372,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -285,7 +401,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -297,17 +413,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -334,6 +450,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -351,6 +479,30 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" @@ -363,6 +515,11 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -375,12 +532,42 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -390,6 +577,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -417,3 +609,8 @@ whatwg-url@^5.0.0: dependencies: tr46 "~0.0.3" webidl-conversions "^3.0.0" + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index 5a9ccd93d30..cd9aef49677 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -60,6 +60,7 @@ function withNodeDefaults(/**@type WebpackConfig & { context: string }*/extConfi externals: { 'vscode': 'commonjs vscode', // ignored because it doesn't exist, 'applicationinsights-native-metrics': 'commonjs applicationinsights-native-metrics', // ignored because we don't ship native module + '@azure/functions-core': 'commonjs azure/functions-core', // optioinal dependency of appinsights that we don't use '@opentelemetry/tracing': 'commonjs @opentelemetry/tracing', // ignored because we don't ship this module '@opentelemetry/instrumentation': 'commonjs @opentelemetry/instrumentation', // ignored because we don't ship this module '@azure/opentelemetry-instrumentation-azure-sdk': 'commonjs @azure/opentelemetry-instrumentation-azure-sdk', // ignored because we don't ship this module @@ -143,6 +144,7 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo externals: { 'vscode': 'commonjs vscode', // ignored because it doesn't exist, 'applicationinsights-native-metrics': 'commonjs applicationinsights-native-metrics', // ignored because we don't ship native module + '@azure/functions-core': 'commonjs azure/functions-core', // optioinal dependency of appinsights that we don't use '@opentelemetry/tracing': 'commonjs @opentelemetry/tracing', // ignored because we don't ship this module '@opentelemetry/instrumentation': 'commonjs @opentelemetry/instrumentation', // ignored because we don't ship this module '@azure/opentelemetry-instrumentation-azure-sdk': 'commonjs @azure/opentelemetry-instrumentation-azure-sdk', // ignored because we don't ship this module diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index 79abe8e57cd..e86b8de070b 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -66,7 +66,7 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "0.7.5" + "@vscode/extension-telemetry": "^0.8.4" }, "devDependencies": { "@types/vscode-webview": "^1.57.0", diff --git a/extensions/simple-browser/yarn.lock b/extensions/simple-browser/yarn.lock index e57ce16ff44..d6ce34c9e21 100644 --- a/extensions/simple-browser/yarn.lock +++ b/extensions/simple-browser/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,59 +185,109 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + "@types/vscode-webview@^1.57.0": version "1.57.0" resolved "https://registry.yarnpkg.com/@types/vscode-webview/-/vscode-webview-1.57.0.tgz#bad5194d45ae8d03afc1c0f67f71ff5e7a243bbf" integrity sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -187,22 +296,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -224,6 +335,11 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -248,7 +364,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -260,17 +376,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -288,6 +404,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -305,6 +433,30 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -317,17 +469,52 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -337,6 +524,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tslib@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -351,3 +543,8 @@ vscode-codicons@^0.0.14: version "0.0.14" resolved "https://registry.yarnpkg.com/vscode-codicons/-/vscode-codicons-0.0.14.tgz#e0d05418e2e195564ff6f6a2199d70415911c18f" integrity sha512-6CEH5KT9ct5WMw7n5dlX7rB8ya4CUI2FSq1Wk36XaW+c5RglFtAanUV0T+gvZVVFhl/WxfjTvFHq06Hz9c1SLA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index d855af70e68..e3c9a7f870d 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -33,7 +33,7 @@ "Programming Languages" ], "dependencies": { - "@vscode/extension-telemetry": "0.7.5", + "@vscode/extension-telemetry": "^0.8.4", "jsonc-parser": "^3.2.0", "semver": "7.5.2", "vscode-tas-client": "^0.1.63", diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index 1b011de0449..eedf2d9c580 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,39 +185,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -175,15 +269,20 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45" integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ== -"@vscode/extension-telemetry@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" "@vscode/sync-api-client@^0.7.2": version "0.7.2" @@ -206,6 +305,16 @@ "@vscode/sync-api-common" "0.7.2" vscode-uri "3.0.3" +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -213,22 +322,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -257,6 +368,11 @@ axios@^0.26.1: dependencies: follow-redirects "^1.14.8" +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -281,7 +397,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -293,17 +409,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -326,6 +442,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -343,6 +471,23 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + jsonc-parser@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" @@ -367,11 +512,39 @@ mime-types@^2.1.12: dependencies: mime-db "1.52.0" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@7.5.2: version "7.5.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.2.tgz#5b851e66d1be07c1cdaf37dfc856f543325a2beb" @@ -384,7 +557,14 @@ semver@^5.3.0, semver@^5.4.1: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -shimmer@^1.1.0, shimmer@^1.2.0: +semver@^7.5.1, semver@^7.5.3: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -394,6 +574,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tas-client@0.1.58: version "0.1.58" resolved "https://registry.yarnpkg.com/tas-client/-/tas-client-0.1.58.tgz#67d66bf0e27df5276ebc751105e6ad47791c36d8" From bd813b9954edeb9250ded98170890b74afddaf80 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 13:07:34 -0700 Subject: [PATCH 174/607] Clean up --- src/vs/workbench/api/browser/mainThreadTerminalService.ts | 6 ++---- src/vs/workbench/contrib/terminal/browser/terminal.ts | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index a4247e13a3f..c9105a64c12 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -42,6 +42,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape private readonly _profileProviders = new Map(); private readonly _quickFixProviders = new Map(); private readonly _dataEventTracker = new MutableDisposable(); + private readonly _sendCommandEventListener = new MutableDisposable(); /** * A single shared terminal link provider for the exthost. When an ext registers a link * provider, this is registered with the terminal on the renderer side and all links are @@ -229,8 +230,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._dataEventTracker.clear(); } - private _sendCommandEventListener = new MutableDisposable(); - public $startSendingCommandEvents(): void { this._logService.info('$startSendingCommandEvents'); if (this._sendCommandEventListener.value) { @@ -238,8 +237,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } const multiplexer = this._terminalService.createInstanceCapabilityEventMultiplexer(TerminalCapability.CommandDetection, capability => capability.onCommandFinished); - this._sendCommandEventListener.value = multiplexer; - multiplexer.event(e => { this._onDidExecuteCommand(e.instance.instanceId, { commandLine: e.data.command, @@ -251,6 +248,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape } }); }); + this._sendCommandEventListener.value = multiplexer; } public $stopSendingCommandEvents(): void { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index d418b94263f..793da5a567f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -274,6 +274,7 @@ export interface ITerminalService extends ITerminalInstanceHost { /** * Creates a capability event listener that listens to capabilities on all instances, * dynamically adding and removing instances and capabilities as needed. + * @param capabilityId The capability type to listen to an event on. * @param getEvent Maps the capability to the event. */ createInstanceCapabilityEventMultiplexer(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> }; From 0d474f0081ef0f9f59ef34eb530614d157c761a0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 13:55:41 -0700 Subject: [PATCH 175/607] Simplify multiplexer event names --- src/vs/workbench/api/browser/mainThreadTerminalService.ts | 2 +- src/vs/workbench/contrib/terminal/browser/terminal.ts | 4 ++-- .../workbench/contrib/terminal/browser/terminalService.ts | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index c9105a64c12..b79eb926d98 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -236,7 +236,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape return; } - const multiplexer = this._terminalService.createInstanceCapabilityEventMultiplexer(TerminalCapability.CommandDetection, capability => capability.onCommandFinished); + const multiplexer = this._terminalService.onInstanceCapabilityEvent(TerminalCapability.CommandDetection, capability => capability.onCommandFinished); multiplexer.event(e => { this._onDidExecuteCommand(e.instance.instanceId, { commandLine: e.data.command, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 793da5a567f..ec1a5df552f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -269,7 +269,7 @@ export interface ITerminalService extends ITerminalInstanceHost { * instances and removing old instances as needed. * @param getEvent Maps the instance to the event. */ - createInstanceEventMultiplexer(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event }; + onInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event }; /** * Creates a capability event listener that listens to capabilities on all instances, @@ -277,7 +277,7 @@ export interface ITerminalService extends ITerminalInstanceHost { * @param capabilityId The capability type to listen to an event on. * @param getEvent Maps the capability to the event. */ - createInstanceCapabilityEventMultiplexer(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> }; + onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> }; } export class TerminalLinkQuickPickEvent extends MouseEvent { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index b3bb2d1d2cb..c9f6c946dc2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -1196,7 +1196,7 @@ export class TerminalService extends Disposable implements ITerminalService { this._editingTerminal = instance; } - createInstanceEventMultiplexer(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event } { + onInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event } { const store = new DisposableStore(); const multiplexer = store.add(new EventMultiplexer()); const instanceListeners = store.add(new DisposableMap()); @@ -1227,7 +1227,7 @@ export class TerminalService extends Disposable implements ITerminalService { }; } - createInstanceCapabilityEventMultiplexer(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { + onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { const store = new DisposableStore(); const multiplexer = store.add(new EventMultiplexer<{ instance: ITerminalInstance; data: K }>()); const capabilityListeners = store.add(new DisposableMap()); @@ -1246,7 +1246,7 @@ export class TerminalService extends Disposable implements ITerminalService { } // Added capabilities - const addCapabilityMultiplexer = this.createInstanceEventMultiplexer(instance => Event.map(instance.capabilities.onDidAddCapability2, changeEvent => ({ instance, changeEvent }))); + const addCapabilityMultiplexer = this.onInstanceEvent(instance => Event.map(instance.capabilities.onDidAddCapability2, changeEvent => ({ instance, changeEvent }))); addCapabilityMultiplexer.event(e => { if (e.changeEvent.id === capabilityId) { addCapability(e.instance, e.changeEvent.capability); @@ -1254,7 +1254,7 @@ export class TerminalService extends Disposable implements ITerminalService { }); // Removed capabilities - const removeCapabilityMultiplexer = this.createInstanceEventMultiplexer(instance => instance.capabilities.onDidRemoveCapability2); + const removeCapabilityMultiplexer = this.onInstanceEvent(instance => instance.capabilities.onDidRemoveCapability2); removeCapabilityMultiplexer.event(e => { if (e.id === capabilityId) { capabilityListeners.deleteAndDispose(e.capability); From ee8edc4dc9e6cae4f879bd6ad9e05a9ab40e07ee Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 22:32:45 +0200 Subject: [PATCH 176/607] Better moved code detection. --- src/vs/editor/common/diff/advancedLinesDiffComputer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts index 61fd21d9c29..a7d9605d78c 100644 --- a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts @@ -311,7 +311,7 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { const modifiedDist = current.modified.startLineNumber - last.modified.endLineNumberExclusive; const currentMoveAfterLast = originalDist >= 0 && modifiedDist >= 0; - if (currentMoveAfterLast && originalDist <= 1 && modifiedDist <= 1) { + if (currentMoveAfterLast && originalDist + modifiedDist <= 2) { joinedMoves[joinedMoves.length - 1] = last.join(current); continue; } From 9e2cd079528bea00be8aabb16df1323683bb382d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 22:38:58 +0200 Subject: [PATCH 177/607] Use text instead of icon for moved code compare button --- .../browser/widget/diffEditorWidget2/movedBlocksLines.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 6a5316decba..f52c55e389f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -325,6 +325,7 @@ class MovedBlockOverlayWidget extends ViewZoneOverlayWidget { const isActive = this._diffModel.movedTextToCompare.read(reader) === _move; actionCompare.checked = isActive; })); - actionBar.push(actionCompare, { icon: true, label: false }); + + actionBar.push(actionCompare, { icon: false, label: true }); } } From 506e14d8db23cad93f15781ec2405780cab4d35a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 22:55:21 +0200 Subject: [PATCH 178/607] Fixes #191255 --- .../editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index f52c55e389f..c4144924bce 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -145,7 +145,7 @@ export class MovedBlocksLinesPart extends Disposable { lines.sort(tieBreakComparators( compareBy(l => l.fromWithoutScroll > l.toWithoutScroll, booleanComparator), - compareBy(l => -l.fromWithoutScroll, numberComparator) + compareBy(l => l.fromWithoutScroll > l.toWithoutScroll ? l.fromWithoutScroll : -l.toWithoutScroll, numberComparator) )); const layout = LinesLayout.compute(lines.map(l => l.range)); From 9cd4c084dfd2cc9ef3c41d615898646b6c3f2896 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 24 Aug 2023 14:43:06 -0700 Subject: [PATCH 179/607] reset context keys --- .../contrib/accessibility/browser/accessibleView.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 506af0bfe15..02c1f17b6f6 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -170,6 +170,16 @@ class AccessibleView extends Disposable { this._updateToolbar(this._currentProvider.actions, this._currentProvider.options.type); } })); + this._register(this._editorWidget.onDidDispose(() => this._resetContextKeys())); + } + + private _resetContextKeys(): void { + this._accessiblityHelpIsShown.reset(); + this._accessibleViewIsShown.reset(); + this._accessibleViewSupportsNavigation.reset(); + this._accessibleViewVerbosityEnabled.reset(); + this._accessibleViewGoToSymbolSupported.reset(); + this._accessibleViewCurrentProviderId.reset(); } show(provider?: IAccessibleContentProvider, symbol?: IAccessibleViewSymbol, showAccessibleViewHelp?: boolean): void { @@ -186,8 +196,7 @@ class AccessibleView extends Disposable { onHide: () => { if (!showAccessibleViewHelp) { this._currentProvider = undefined; - this._accessibleViewCurrentProviderId.reset(); - this._updateContextKeys(provider!, false); + this._resetContextKeys(); } } }; From 27eb58d046191d391bbf99a18398fdebe39e7996 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Thu, 24 Aug 2023 14:54:28 -0700 Subject: [PATCH 180/607] Add `preserveInput` for quick search and remove col:line (#191260) Fixes #191258 Fixes #191253 --- .../quickTextSearch/textSearchQuickAccess.ts | 14 ++++++++++---- .../contrib/search/browser/search.contribution.ts | 7 ++++++- src/vs/workbench/services/search/common/search.ts | 3 +++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index d01ad5ad73b..6324613fbfd 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -15,9 +15,9 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchCompressibleObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; import { FastAndSlowPicks, IPickerQuickAccessItem, PickerQuickAccessProvider, Picks } from 'vs/platform/quickinput/browser/pickerQuickAccess'; +import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; import { IKeyMods, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { IWorkbenchQuickAccessConfiguration } from 'vs/workbench/browser/quickaccess'; import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/common/views'; import { searchDetailsIcon, searchOpenInFileIcon } from 'vs/workbench/contrib/search/browser/searchIcons'; @@ -78,16 +78,23 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider().workbench?.editor; const searchConfig = this._configurationService.getValue().search; - const quickAccessConfig = this._configurationService.getValue().workbench.quickOpen; return { openEditorPinned: !editorConfig?.enablePreviewFromQuickOpen || !editorConfig?.enablePreview, - preserveInput: quickAccessConfig.preserveInput, + preserveInput: searchConfig.experimental.quickAccess.preserveInput, maxResults: searchConfig.maxResults, smartCase: searchConfig.smartCase, }; } + get defaultFilterValue(): DefaultQuickAccessFilterValue | undefined { + if (this.configuration.preserveInput) { + return DefaultQuickAccessFilterValue.LAST; + } + + return undefined; + } + private doSearch(contentPattern: string, token: CancellationToken): { syncResults: FileMatch[]; asyncResults: Promise; @@ -195,7 +202,6 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { await this.handleAccept(fileMatch, { diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 2f9b6d54d73..a2f41a98fac 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -376,7 +376,12 @@ configurationRegistry.registerConfiguration({ type: 'boolean', description: nls.localize('search.experimental.closedNotebookResults', "Show notebook editor rich content results for closed notebooks. Please refresh your search results after changing this setting."), default: false - } + }, + 'search.experimental.quickAccess.preserveInput': { + 'type': 'boolean', + 'description': nls.localize('search.experimental.quickAccess.preserveInput', "Controls whether the last typed input to Quick Search should be restored when opening it the next time."), + 'default': false + }, } }); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index caded264a85..6935850b671 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -414,6 +414,9 @@ export interface ISearchConfigurationProperties { defaultViewMode: ViewMode; experimental: { closedNotebookRichContentResults: boolean; + quickAccess: { + preserveInput: boolean; + }; }; } From 885ebcd9b44968dbad137ff21102a39341cba303 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 22:25:19 +0200 Subject: [PATCH 181/607] Fixes #185781 --- .../diffEditorWidget2/diffEditorWidget2.ts | 8 +- .../widget/diffEditorWidget2/outlineModel.ts | 384 ++++++++++++++++++ .../diffEditorWidget2/unchangedRanges.ts | 104 ++++- 3 files changed, 480 insertions(+), 16 deletions(-) create mode 100644 src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 280c2bb7b9a..b8c42ae717f 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -157,7 +157,9 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._register(autorunWithStore((reader, store) => { /** @description UnchangedRangesFeature */ - this.unchangedRangesFeature = store.add(new (readHotReloadableExport(UnchangedRangesFeature, reader))(this._editors, this._diffModel, this._options)); + this.unchangedRangesFeature = store.add( + this._instantiationService.createInstance(readHotReloadableExport(UnchangedRangesFeature, reader), this._editors, this._diffModel, this._options) + ); })); this._register(autorunWithStore((reader, store) => { @@ -178,7 +180,9 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._register(autorunWithStore((reader, store) => { /** @description OverviewRulerPart */ - store.add(this._instantiationService.createInstance(readHotReloadableExport(OverviewRulerPart, reader), this._editors, + store.add(this._instantiationService.createInstance( + readHotReloadableExport(OverviewRulerPart, reader), + this._editors, this.elements.root, this._diffModel, this._rootSizeObserver.width, diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts new file mode 100644 index 00000000000..cd12277b82c --- /dev/null +++ b/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts @@ -0,0 +1,384 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { binarySearch, coalesceInPlace, equals } from 'vs/base/common/arrays'; +import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; +import { onUnexpectedExternalError } from 'vs/base/common/errors'; +import { Iterable } from 'vs/base/common/iterator'; +import { commonPrefixLength } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { IPosition, Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; +import { DocumentSymbol, DocumentSymbolProvider } from 'vs/editor/common/languages'; +import { ITextModel } from 'vs/editor/common/model'; +import { MarkerSeverity } from 'vs/platform/markers/common/markers'; + +// TODO@hediet: These classes are copied from outlineModel.ts because of layering issues. +// Because these classes just depend on the DocumentSymbolProvider (which is in the core editor), +// they should be moved to the core editor as well. + +export abstract class TreeElement { + + abstract id: string; + abstract children: Map; + abstract parent: TreeElement | undefined; + + remove(): void { + this.parent?.children.delete(this.id); + } + + static findId(candidate: DocumentSymbol | string, container: TreeElement): string { + // complex id-computation which contains the origin/extension, + // the parent path, and some dedupe logic when names collide + let candidateId: string; + if (typeof candidate === 'string') { + candidateId = `${container.id}/${candidate}`; + } else { + candidateId = `${container.id}/${candidate.name}`; + if (container.children.get(candidateId) !== undefined) { + candidateId = `${container.id}/${candidate.name}_${candidate.range.startLineNumber}_${candidate.range.startColumn}`; + } + } + + let id = candidateId; + for (let i = 0; container.children.get(id) !== undefined; i++) { + id = `${candidateId}_${i}`; + } + + return id; + } + + static getElementById(id: string, element: TreeElement): TreeElement | undefined { + if (!id) { + return undefined; + } + const len = commonPrefixLength(id, element.id); + if (len === id.length) { + return element; + } + if (len < element.id.length) { + return undefined; + } + for (const [, child] of element.children) { + const candidate = TreeElement.getElementById(id, child); + if (candidate) { + return candidate; + } + } + return undefined; + } + + static size(element: TreeElement): number { + let res = 1; + for (const [, child] of element.children) { + res += TreeElement.size(child); + } + return res; + } + + static empty(element: TreeElement): boolean { + return element.children.size === 0; + } +} + +export interface IOutlineMarker { + startLineNumber: number; + startColumn: number; + endLineNumber: number; + endColumn: number; + severity: MarkerSeverity; +} + +export class OutlineElement extends TreeElement { + + children = new Map(); + marker: { count: number; topSev: MarkerSeverity } | undefined; + + constructor( + readonly id: string, + public parent: TreeElement | undefined, + readonly symbol: DocumentSymbol + ) { + super(); + } +} + +export class OutlineGroup extends TreeElement { + + children = new Map(); + + constructor( + readonly id: string, + public parent: TreeElement | undefined, + readonly label: string, + readonly order: number, + ) { + super(); + } + + getItemEnclosingPosition(position: IPosition): OutlineElement | undefined { + return position ? this._getItemEnclosingPosition(position, this.children) : undefined; + } + + private _getItemEnclosingPosition(position: IPosition, children: Map): OutlineElement | undefined { + for (const [, item] of children) { + if (!item.symbol.range || !Range.containsPosition(item.symbol.range, position)) { + continue; + } + return this._getItemEnclosingPosition(position, item.children) || item; + } + return undefined; + } + + updateMarker(marker: IOutlineMarker[]): void { + for (const [, child] of this.children) { + this._updateMarker(marker, child); + } + } + + private _updateMarker(markers: IOutlineMarker[], item: OutlineElement): void { + item.marker = undefined; + + // find the proper start index to check for item/marker overlap. + const idx = binarySearch(markers, item.symbol.range, Range.compareRangesUsingStarts); + let start: number; + if (idx < 0) { + start = ~idx; + if (start > 0 && Range.areIntersecting(markers[start - 1], item.symbol.range)) { + start -= 1; + } + } else { + start = idx; + } + + const myMarkers: IOutlineMarker[] = []; + let myTopSev: MarkerSeverity | undefined; + + for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) { + // remove markers intersecting with this outline element + // and store them in a 'private' array. + const marker = markers[start]; + myMarkers.push(marker); + (markers as Array)[start] = undefined; + if (!myTopSev || marker.severity > myTopSev) { + myTopSev = marker.severity; + } + } + + // Recurse into children and let them match markers that have matched + // this outline element. This might remove markers from this element and + // therefore we remember that we have had markers. That allows us to render + // the dot, saying 'this element has children with markers' + for (const [, child] of item.children) { + this._updateMarker(myMarkers, child); + } + + if (myTopSev) { + item.marker = { + count: myMarkers.length, + topSev: myTopSev + }; + } + + coalesceInPlace(markers); + } +} + +export class OutlineModel extends TreeElement { + + static create(registry: LanguageFeatureRegistry, textModel: ITextModel, token: CancellationToken): Promise { + + const cts = new CancellationTokenSource(token); + const result = new OutlineModel(textModel.uri); + const provider = registry.ordered(textModel); + const promises = provider.map((provider, index) => { + + const id = TreeElement.findId(`provider_${index}`, result); + const group = new OutlineGroup(id, result, provider.displayName ?? 'Unknown Outline Provider', index); + + + return Promise.resolve(provider.provideDocumentSymbols(textModel, cts.token)).then(result => { + for (const info of result || []) { + OutlineModel._makeOutlineElement(info, group); + } + return group; + }, err => { + onUnexpectedExternalError(err); + return group; + }).then(group => { + if (!TreeElement.empty(group)) { + result._groups.set(id, group); + } else { + group.remove(); + } + }); + }); + + const listener = registry.onDidChange(() => { + const newProvider = registry.ordered(textModel); + if (!equals(newProvider, provider)) { + cts.cancel(); + } + }); + + return Promise.all(promises).then(() => { + if (cts.token.isCancellationRequested && !token.isCancellationRequested) { + return OutlineModel.create(registry, textModel, token); + } else { + return result._compact(); + } + }).finally(() => { + listener.dispose(); + }); + } + + private static _makeOutlineElement(info: DocumentSymbol, container: OutlineGroup | OutlineElement): void { + const id = TreeElement.findId(info, container); + const res = new OutlineElement(id, container, info); + if (info.children) { + for (const childInfo of info.children) { + OutlineModel._makeOutlineElement(childInfo, res); + } + } + container.children.set(res.id, res); + } + + static get(element: TreeElement | undefined): OutlineModel | undefined { + while (element) { + if (element instanceof OutlineModel) { + return element; + } + element = element.parent; + } + return undefined; + } + + readonly id = 'root'; + readonly parent = undefined; + + protected _groups = new Map(); + children = new Map(); + + protected constructor(readonly uri: URI) { + super(); + + this.id = 'root'; + this.parent = undefined; + } + + private _compact(): this { + let count = 0; + for (const [key, group] of this._groups) { + if (group.children.size === 0) { // empty + this._groups.delete(key); + } else { + count += 1; + } + } + if (count !== 1) { + // + this.children = this._groups; + } else { + // adopt all elements of the first group + const group = Iterable.first(this._groups.values())!; + for (const [, child] of group.children) { + child.parent = this; + this.children.set(child.id, child); + } + } + return this; + } + + merge(other: OutlineModel): boolean { + if (this.uri.toString() !== other.uri.toString()) { + return false; + } + if (this._groups.size !== other._groups.size) { + return false; + } + this._groups = other._groups; + this.children = other.children; + return true; + } + + getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement | undefined { + + let preferredGroup: OutlineGroup | undefined; + if (context) { + let candidate = context.parent; + while (candidate && !preferredGroup) { + if (candidate instanceof OutlineGroup) { + preferredGroup = candidate; + } + candidate = candidate.parent; + } + } + + let result: OutlineElement | undefined = undefined; + for (const [, group] of this._groups) { + result = group.getItemEnclosingPosition(position); + if (result && (!preferredGroup || preferredGroup === group)) { + break; + } + } + return result; + } + + getItemById(id: string): TreeElement | undefined { + return TreeElement.getElementById(id, this); + } + + updateMarker(marker: IOutlineMarker[]): void { + // sort markers by start range so that we can use + // outline element starts for quicker look up + marker.sort(Range.compareRangesUsingStarts); + + for (const [, group] of this._groups) { + group.updateMarker(marker.slice(0)); + } + } + + getTopLevelSymbols(): DocumentSymbol[] { + const roots: DocumentSymbol[] = []; + for (const child of this.children.values()) { + if (child instanceof OutlineElement) { + roots.push(child.symbol); + } else { + roots.push(...Iterable.map(child.children.values(), child => child.symbol)); + } + } + return roots.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + } + + asListOfDocumentSymbols(): DocumentSymbol[] { + const roots = this.getTopLevelSymbols(); + const bucket: DocumentSymbol[] = []; + OutlineModel._flattenDocumentSymbols(bucket, roots, ''); + return bucket.sort((a, b) => + Position.compare(Range.getStartPosition(a.range), Range.getStartPosition(b.range)) || Position.compare(Range.getEndPosition(b.range), Range.getEndPosition(a.range)) + ); + } + + private static _flattenDocumentSymbols(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideContainerLabel: string): void { + for (const entry of entries) { + bucket.push({ + kind: entry.kind, + tags: entry.tags, + name: entry.name, + detail: entry.detail, + containerName: entry.containerName || overrideContainerLabel, + range: entry.range, + selectionRange: entry.selectionRange, + children: undefined, // we flatten it... + }); + + // Recurse over children + if (entry.children) { + OutlineModel._flattenDocumentSymbols(bucket, entry.children, entry.name); + } + } + } +} diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 7272f56b11f..b3ac905331d 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -4,34 +4,49 @@ *--------------------------------------------------------------------------------------------*/ import { $, addDisposableListener, h, reset } from 'vs/base/browser/dom'; -import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { renderIcon, renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { compareBy, numberComparator, reverseOrder } from 'vs/base/common/arrays'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; +import { Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, derived, derivedWithStore, observableFromEvent, transaction } from 'vs/base/common/observable'; +import { IObservable, IReader, autorun, autorunWithStore, derived, derivedWithStore, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { isDefined } from 'vs/base/common/types'; import { ICodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions'; import { DiffEditorViewModel, UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; +import { OutlineModel } from 'vs/editor/browser/widget/diffEditorWidget2/outlineModel'; import { PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; -import { IModelDecorationOptions, IModelDeltaDecoration } from 'vs/editor/common/model'; +import { SymbolKind, SymbolKinds } from 'vs/editor/common/languages'; +import { IModelDecorationOptions, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { localize } from 'vs/nls'; export class UnchangedRangesFeature extends Disposable { private _isUpdatingViewZones = false; public get isUpdatingViewZones(): boolean { return this._isUpdatingViewZones; } + private readonly _modifiedModel = observableFromEvent(this._editors.modified.onDidChangeModel, () => this._editors.modified.getModel()); + + private readonly _modifiedOutlineSource = derivedWithStore('modified outline source', (reader, store) => { + const m = this._modifiedModel.read(reader); + if (!m) { return undefined; } + return store.add(new OutlineSource(this._languageFeaturesService, m)); + }); + constructor( private readonly _editors: DiffEditorEditors, private readonly _diffModel: IObservable, private readonly _options: DiffEditorOptions, + @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, ) { super(); @@ -66,6 +81,9 @@ export class UnchangedRangesFeature extends Disposable { const modViewZones: IViewZone[] = []; const sideBySide = this._options.renderSideBySide.read(reader); + const modifiedOutlineSource = this._modifiedOutlineSource.read(reader); + if (!modifiedOutlineSource) { return { origViewZones, modViewZones }; } + const curUnchangedRegions = unchangedRegions.read(reader); for (const r of curUnchangedRegions) { if (r.shouldHideControls(reader)) { @@ -76,13 +94,13 @@ export class UnchangedRangesFeature extends Disposable { const d = derived(reader => /** @description hiddenOriginalRangeStart */ r.getHiddenOriginalRange(reader).startLineNumber - 1); const origVz = new PlaceholderViewZone(d, 24); origViewZones.push(origVz); - store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, !sideBySide)); + store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, !sideBySide, modifiedOutlineSource)); } { const d = derived(reader => /** @description hiddenModifiedRangeStart */ r.getHiddenModifiedRange(reader).startLineNumber - 1); const modViewZone = new PlaceholderViewZone(d, 24); modViewZones.push(modViewZone); - store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, false)); + store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, false, modifiedOutlineSource)); } } @@ -177,6 +195,57 @@ export class UnchangedRangesFeature extends Disposable { } } +class DisposableCancellationTokenSource extends CancellationTokenSource { + public override dispose() { + super.dispose(true); + } +} + +class OutlineSource extends Disposable { + private readonly _currentModel = observableValue('current model', undefined); + + constructor( + @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, + private readonly _textModel: ITextModel, + ) { + super(); + + const documentSymbolProviderChanged = observableSignalFromEvent( + 'documentSymbolProvider.onDidChange', + this._languageFeaturesService.documentSymbolProvider.onDidChange + ); + + const textModelChanged = observableSignalFromEvent( + '_textModel.onDidChangeContent', + Event.debounce(e => this._textModel.onDidChangeContent(e), () => undefined, 100) + ); + + this._register(autorunWithStore(async (reader, store) => { + documentSymbolProviderChanged.read(reader); + textModelChanged.read(reader); + + const src = store.add(new DisposableCancellationTokenSource()); + const model = await OutlineModel.create( + this._languageFeaturesService.documentSymbolProvider, + this._textModel, + src.token, + ); + if (store.isDisposed) { return; } + + this._currentModel.set(model, undefined); + })); + } + + public getBreadcrumbItems(startRange: LineRange, reader: IReader): { name: string; kind: SymbolKind }[] { + const m = this._currentModel.read(reader); + if (!m) { return []; } + const symbols = m.asListOfDocumentSymbols() + .filter(s => startRange.contains(s.range.startLineNumber) && !startRange.contains(s.range.endLineNumber)); + symbols.sort(reverseOrder(compareBy(s => s.range.endLineNumber - s.range.startLineNumber, numberComparator))); + return symbols.map(s => ({ name: s.name, kind: s.kind })); + } +} + class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { private readonly _nodes = h('div.diff-hidden-lines', [ h('div.top@top', { title: localize('diff.hiddenLines.top', 'Click or drag to show more above') }), @@ -194,6 +263,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { _viewZone: PlaceholderViewZone, private readonly _unchangedRegion: UnchangedRegion, private readonly hide: boolean, + private readonly _modifiedOutlineSource: OutlineSource, ) { const root = h('div.diff-hidden-lines-widget'); super(_editor, _viewZone, root.root); @@ -296,19 +366,25 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { children.push($('span', { title: linesHiddenText }, linesHiddenText)); } - // TODO@hediet implement breadcrumbs for collapsed regions - /* - if (_unchangedRegion.originalLineNumber === 48) { + const range = this._unchangedRegion.getHiddenModifiedRange(reader); + const items = this._modifiedOutlineSource.getBreadcrumbItems(range, reader); + + if (items.length > 0) { children.push($('span', undefined, '\u00a0|\u00a0')); - children.push($('span', { title: 'test' }, ...renderLabelWithIcons('$(symbol-class) DiffEditorWidget2'))); - } else if (_unchangedRegion.originalLineNumber === 88) { - children.push($('span', undefined, '\u00a0|\u00a0')); - children.push($('span', { title: 'test' }, ...renderLabelWithIcons('$(symbol-constructor) constructor'))); + + let isFirst = true; + for (const item of items) { + if (!isFirst) { + children.push($('span', {}, ' ', renderIcon(Codicon.chevronRight), ' ')); + } + + const icon = SymbolKinds.toIcon(item.kind); + children.push($('span', {}, renderIcon(icon), ' ', item.name)); + isFirst = false; + } } - */ reset(this._nodes.others, ...children); - })); } } From 16cae5ea488e45d183859c3b5dcca7e7af46ae83 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 24 Aug 2023 15:35:08 -0700 Subject: [PATCH 182/607] Feedback --- src/vs/workbench/api/common/extHostTerminalService.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index ecc9e00556f..58bc387e600 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -24,7 +24,6 @@ import { ThemeColor } from 'vs/base/common/themables'; import { Promises } from 'vs/base/common/async'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; import { TerminalQuickFix, ViewColumn } from 'vs/workbench/api/common/extHostTypeConverters'; -import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, IDisposable { @@ -858,7 +857,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public getEnvironmentVariableCollection(extension: IExtensionDescription): IEnvironmentVariableCollection { let collection = this._environmentVariableCollections.get(extension.identifier.value); if (!collection) { - collection = new UnifiedEnvironmentVariableCollection(extension); + collection = new UnifiedEnvironmentVariableCollection(); this._setEnvironmentVariableCollection(extension.identifier.value, collection); } return collection.getScopedEnvironmentVariableCollection(undefined); @@ -873,7 +872,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void { collections.forEach(entry => { const extensionIdentifier = entry[0]; - const collection = new UnifiedEnvironmentVariableCollection(undefined, entry[1]); + const collection = new UnifiedEnvironmentVariableCollection(entry[1]); this._setEnvironmentVariableCollection(extensionIdentifier, collection); }); } @@ -918,11 +917,6 @@ class UnifiedEnvironmentVariableCollection { get onDidChangeCollection(): Event { return this._onDidChangeCollection && this._onDidChangeCollection.event; } constructor( - // HACK: Only check proposed options if extension is set (when the collection is not - // restored by serialization). This saves us from getting the extension details and - // shouldn't ever happen since you can only set them initially via the proposed check. - // TODO: This should be removed when the env var extension API(s) are stabilized - private readonly _extension: IExtensionDescription | undefined, serialized?: ISerializableEnvironmentVariableCollection ) { this.map = new Map(serialized); From 99be1c1421fea67ca92ace86a232ba7178592216 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Aug 2023 15:38:47 -0700 Subject: [PATCH 183/607] Pick up latest TS (#191262) --- 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 02a1f822e7d..2b68f668d5b 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,7 +4,7 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "^5.2.1-rc" + "typescript": "^5.2.2" }, "scripts": { "postinstall": "node ./postinstall.mjs" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index 08ae5fb06c3..b704d5f8a28 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -228,10 +228,10 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -typescript@^5.2.1-rc: - version "5.2.1-rc" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.1-rc.tgz#9cf33ff6bc39ba9e1fa59761124f596ecf5e0c07" - integrity sha512-gsOdmedQZEWLrYhNqHuzPmcV+4wX7UujzYqszDC5mVMjcN6Nm7lN2eAtndmjWl24aGdAwJqL2ooywkxpaTx8QQ== +typescript@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== vscode-grammar-updater@^1.1.0: version "1.1.0" From f9db0056398ae3f24ebd0a6b38be13b212f95c6e Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 24 Aug 2023 15:53:44 -0700 Subject: [PATCH 184/607] Bump height of quick chat to 900 max (#191265) --- src/vs/workbench/contrib/chat/browser/chatQuick.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 6e30b77907b..4ddde25c475 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -149,7 +149,7 @@ class QuickChat extends Disposable { })); this.widget.render(parent); this.widget.setVisible(true); - this.widget.setDynamicChatTreeItemLayout(2, 600); + this.widget.setDynamicChatTreeItemLayout(2, 900); this.updateModel(); if (this._currentQuery) { this.widget.inputEditor.setSelection({ From 37e5ca3fa5eb7db431e74de62ac0b394c5fa70d4 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 24 Aug 2023 16:25:04 -0700 Subject: [PATCH 185/607] Pick up latest TS for building VS Code (#191264) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2e5dcf01a2e..080af3cecd2 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.3.0-dev.20230816", + "typescript": "^5.3.0-dev.20230824", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/yarn.lock b/yarn.lock index 5595e14de37..582d6e845a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10075,10 +10075,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.3.0-dev.20230816: - version "5.3.0-dev.20230816" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.0-dev.20230816.tgz#409982c629164811db1eb62b365ed2e1b526458d" - integrity sha512-iEOudrx61DsbJn+z2bVX+/FldF7ILAuGwQYO2EvF4F33Q8DUV0KSkiikxUB83VVH8ExkwQHVNdtkr16wd2V71w== +typescript@^5.3.0-dev.20230824: + version "5.3.0-dev.20230824" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.0-dev.20230824.tgz#14fc65c14c588363c0d290dbbbda8ae0968fd95b" + integrity sha512-iiUWxGibzrRHEBLDJfVymsvpPKflf3cMrw0oQTMQoguFS2ikNlVlfQWAsYeHqGpRQc77nSQkzsE9rAHNHqvIjw== typical@^4.0.0: version "4.0.0" From cd5b67db5bdcfaae0d191a51a4e22a9cad061903 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Thu, 24 Aug 2023 17:25:55 -0700 Subject: [PATCH 186/607] Tune quick text search heading styles (#191268) --- src/vs/platform/quickinput/browser/media/quickInput.css | 6 ++++++ src/vs/platform/quickinput/browser/quickInputController.ts | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/quickinput/browser/media/quickInput.css b/src/vs/platform/quickinput/browser/media/quickInput.css index 9bb877f9991..f6c76ccd9b5 100644 --- a/src/vs/platform/quickinput/browser/media/quickInput.css +++ b/src/vs/platform/quickinput/browser/media/quickInput.css @@ -318,3 +318,9 @@ .quick-input-list .monaco-list-row.focused .monaco-keybinding-key { background: none; } + +/* Quick input separators as full-row item */ +.quick-input-list .quick-input-list-separator-as-item { + font-weight: 600; + font-size: 12px; +} diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index 0328a6a6ded..f42e1ff78d1 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -683,7 +683,7 @@ export class QuickInputController extends Disposable { content.push(`.quick-input-list .quick-input-list-separator { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`); } if (this.styles.pickerGroup.pickerGroupForeground) { - content.push(`.quick-input-list .quick-input-list-separator-as-item { color: ${this.styles.pickerGroup.pickerGroupForeground}; }`); + content.push(`.quick-input-list .quick-input-list-separator-as-item { color: var(--vscode-descriptionForeground); }`); } if (this.styles.keybindingLabel.keybindingLabelBackground || From 083fca132543aa91a7e1de2dc23857d70ea56dd3 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 24 Aug 2023 17:29:01 -0700 Subject: [PATCH 187/607] Codesign Debian package for PMC API (#191140) * Codesign Debian package for PMC API * Fix directory name * polish displayName --- build/azure-pipelines/linux/product-build-linux.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 2145e588b1c..4641edf7670 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -312,6 +312,9 @@ steps: continueOnError: true displayName: Download ESRPClient + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/deb '*.deb' + displayName: Codesign deb + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' displayName: Codesign rpm From d8f919404053f3972c8c37797186fc0e6c0b64e9 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Aug 2023 07:23:07 +0200 Subject: [PATCH 188/607] voice - limit media permission to insiders --- src/vs/code/electron-main/app.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 9c79498b8f5..c6536298e05 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -162,9 +162,9 @@ export class CodeApplication extends Disposable { const isUrlFromWebview = (requestingUrl: string | undefined) => requestingUrl?.startsWith(`${Schemas.vscodeWebview}://`); - const allowedPermissionsInMainFrame = new Set([ - 'media' - ]); + const allowedPermissionsInMainFrame = new Set( + this.productService.quality === 'stable' ? [] : ['media'] + ); const allowedPermissionsInWebview = new Set([ 'clipboard-read', From c908b67da5005bb3c42e9b24af0149d8686d1487 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Aug 2023 07:28:09 +0200 Subject: [PATCH 189/607] voice - actions renames --- .../actions/{chatVoiceInputActions.ts => voiceChatActions.ts} | 2 +- .../contrib/chat/electron-sandbox/chat.contribution.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/vs/workbench/contrib/chat/electron-sandbox/actions/{chatVoiceInputActions.ts => voiceChatActions.ts} (99%) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts similarity index 99% rename from src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts rename to src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 9ea3b0cde45..a76572d998f 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -375,7 +375,7 @@ class StopVoiceChatAction extends Action2 { } } -export function registerChatVoiceInputActions() { +export function registerVoiceChatActions() { if (typeof process.env.VSCODE_VOICE_MODULE_PATH === 'string' && product.quality !== 'stable') { // TODO@bpasero package registerAction2(VoiceChatInChatViewAction); registerAction2(QuickVoiceChatAction); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts index 5d49a2d0625..1bd74f66eff 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -3,6 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { registerChatVoiceInputActions } from 'vs/workbench/contrib/chat/electron-sandbox/actions/chatVoiceInputActions'; +import { registerVoiceChatActions } from 'vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions'; -registerChatVoiceInputActions(); +registerVoiceChatActions(); From 8a01424d92fb6860ca17093de36145cc6dd82415 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 08:55:51 +0200 Subject: [PATCH 190/607] placing all the code that resets the state at the beginning of the set state method --- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 6d665ce6484..ba126a2f784 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -106,9 +106,13 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } setState(state: StickyScrollWidgetState): void { + this._stickyLines = []; + this._foldingIconStore.clear(); dom.clearNode(this._lineNumbersDomNode); dom.clearNode(this._linesDomNode); - this._stickyLines = []; + if (!this._editor._getViewModel()) { + return; + } const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); const futureWidgetHeight = state.startLineNumbers.length * editorLineHeight + state.lastLineRelativePosition; @@ -137,10 +141,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private async _renderRootNode(): Promise { - this._foldingIconStore.clear(); - if (!this._editor._getViewModel()) { - return; - } const foldingModel = await FoldingController.get(this._editor)?.getFoldingModel(); const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { From 638d83b40472c3868320ff17ed93e809bb34ae31 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Aug 2023 08:56:41 +0200 Subject: [PATCH 191/607] voice - :lipstick: --- .../actions/voiceChatActions.ts | 30 +++++++++---------- .../workbenchVoiceRecognitionService.ts | 17 +++++++---- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index a76572d998f..8773eff46d5 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -12,7 +12,7 @@ import { equalsIgnoreCase } from 'vs/base/common/strings'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; -import { IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; @@ -28,9 +28,10 @@ import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/action import { ICommandService } from 'vs/platform/commands/common/commands'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import product from 'vs/platform/product/common/product'; +import { ActiveEditorContext } from 'vs/workbench/common/contextkeys'; -const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when there is voice input for chat getting ready.") }); -const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when there is voice input for chat in progress.") }); +const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone.") }); +const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress.") }); interface IVoiceChatSessionController { @@ -94,13 +95,13 @@ class VoiceChatSession { context.focusInput(); const onDidTranscribe = await this.voiceRecognitionService.transcribe(cts.token, { - onDidCancel: () => this.stop() + onDidCancel: () => this.stop(voiceChatSessionId) }); if (cts.token.isCancellationRequested) { return Disposable.None; } - const voiceChatSessionId = this.voiceChatSessionIds++; + const voiceChatSessionId = ++this.voiceChatSessionIds; this.voiceChatGettingReadyKey.set(false); this.voiceChatInProgressKey.set(true); @@ -130,15 +131,9 @@ class VoiceChatSession { } })); - this.currentVoiceChatSession.add(context.onDidAcceptInput(() => { - this.stop(); - })); + this.currentVoiceChatSession.add(context.onDidAcceptInput(() => this.stop(voiceChatSessionId))); - return toDisposable(() => { - if (this.voiceChatSessionIds === voiceChatSessionId) { - this.stop(); - } - }); + return toDisposable(() => this.stop(voiceChatSessionId)); } private isSimilarTranscription(textA: string, textB: string): boolean { @@ -155,11 +150,15 @@ class VoiceChatSession { ); } - stop(): void { + stop(voiceChatSessionId = this.voiceChatSessionIds): void { if (!this.currentVoiceChatSession) { return; } + if (this.voiceChatSessionIds !== voiceChatSessionId) { + return; + } + this.currentVoiceChatSession.dispose(); this.currentVoiceChatSession = undefined; @@ -212,7 +211,7 @@ class InlineVoiceChatAction extends Action2 { original: 'Inline Voice Chat' }, category: CHAT_CATEGORY, - precondition: CONTEXT_PROVIDER_EXISTS, + precondition: ContextKeyExpr.and(CONTEXT_PROVIDER_EXISTS, ActiveEditorContext), f1: true }); } @@ -234,7 +233,6 @@ class InlineVoiceChatAction extends Action2 { const inlineChatSession = controller.run(); const disposable = await VoiceChatSession.getInstance(instantiationService).start(getController(controller)); - inlineChatSession.finally(() => disposable.dispose()); } } diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index c1863701239..d35dd681f77 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -60,19 +60,24 @@ class VoiceTranscriptionWorkletNode extends AudioWorkletNode { } async start(token: CancellationToken): Promise { + token.onCancellationRequested(() => this.stop()); + const sharedProcessConnection = await this.sharedProcessService.createRawConnection(); - token.onCancellationRequested(() => { - this.port.postMessage('vscode:stopVoiceTranscription'); - this.disconnect(); - }); + if (token.isCancellationRequested) { + this.stop(); + return; + } this.port.postMessage('vscode:startVoiceTranscription', [sharedProcessConnection]); } + + private stop(): void { + this.port.postMessage('vscode:stopVoiceTranscription'); + this.disconnect(); + } } -// TODO@voice -// - add native module test to ensure module loads export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognitionService { declare readonly _serviceBrand: undefined; From d4214834ec1052d85a2a42afdbe3c4d89aa0f212 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 09:14:43 +0200 Subject: [PATCH 192/607] setting appropriate scroll top --- .../stickyScroll/browser/stickyScrollWidget.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index ba126a2f784..2f674a55f2a 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -269,7 +269,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const foldingRegions = foldingModel.regions; const indexOfFoldingRegion = foldingRegions.findRange(line); const startLineNumber = foldingRegions.getStartLineNumber(indexOfFoldingRegion); - const endLineNumber = foldingRegions.getEndLineNumber(indexOfFoldingRegion); const isFoldingScope = line === startLineNumber; if (!isFoldingScope) { return; @@ -280,9 +279,14 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); const lineHeight = this._editor.getOption(EditorOption.lineHeight); - const topOfStartLine = this._editor.getTopForLineNumber(startLineNumber) - lineHeight * index + 1; - const topOfEndLine = this._editor.getTopForLineNumber(endLineNumber) - lineHeight * index + 1; - this._editor.setScrollTop(isRegionCollapsed ? topOfStartLine : topOfEndLine); + let scrollTop = - lineHeight * index + 1; + if (isRegionCollapsed) { + scrollTop += this._editor.getTopForLineNumber(startLineNumber); + } else { + const endLineNumber = foldingRegions.getEndLineNumber(indexOfFoldingRegion); + scrollTop += this._editor.getTopForLineNumber(endLineNumber); + } + this._editor.setScrollTop(scrollTop); })); } From 7781ca77732b8deea6336b29639a928d6c63518c Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 09:18:53 +0200 Subject: [PATCH 193/607] saving the line height as a member of the class --- .../browser/stickyScrollWidget.ts | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 2f674a55f2a..c84143d5bce 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -40,6 +40,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private readonly _linesDomNodeScrollable: HTMLElement = document.createElement('div'); private readonly _linesDomNode: HTMLElement = document.createElement('div'); + private _lineHeight: number = this._editor.getOption(EditorOption.lineHeight); private _stickyLines: RenderedStickyLine[] = []; private _lineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; @@ -71,6 +72,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (e.hasChanged(EditorOption.stickyScroll)) { updateScrollLeftPosition(); } + if (e.hasChanged(EditorOption.lineHeight)) { + this._lineHeight = this._editor.getOption(EditorOption.lineHeight); + } })); this._register(this._editor.onDidScrollChange((e) => { if (e.scrollLeftChanged) { @@ -113,8 +117,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!this._editor._getViewModel()) { return; } - const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); - const futureWidgetHeight = state.startLineNumbers.length * editorLineHeight + state.lastLineRelativePosition; + const futureWidgetHeight = state.startLineNumbers.length * this._lineHeight + state.lastLineRelativePosition; if (futureWidgetHeight > 0) { this._lastLineRelativePosition = state.lastLineRelativePosition; @@ -150,8 +153,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._stickyLines.push(renderedStickyLine); } - const editorLineHeight = this._editor.getOption(EditorOption.lineHeight); - const widgetHeight: number = this._lineNumbers.length * editorLineHeight + this._lastLineRelativePosition; + const widgetHeight: number = this._lineNumbers.length * this._lineHeight + this._lastLineRelativePosition; this._rootDomNode.style.display = widgetHeight > 0 ? 'block' : 'none'; this._lineNumbersDomNode.style.height = `${widgetHeight}px`; this._linesDomNodeScrollable.style.height = `${widgetHeight}px`; @@ -172,7 +174,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); const minimapSide = this._editor.getOption(EditorOption.minimap).side; - const lineHeight = this._editor.getOption(EditorOption.lineHeight); const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers); let actualInlineDecorations: LineDecoration[]; @@ -203,12 +204,12 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const lineHTMLNode = document.createElement('span'); lineHTMLNode.className = 'sticky-line-content'; lineHTMLNode.classList.add(`stickyLine${line}`); - lineHTMLNode.style.lineHeight = `${lineHeight}px`; + lineHTMLNode.style.lineHeight = `${this._lineHeight}px`; lineHTMLNode.innerHTML = newLine as string; const lineNumberHTMLNode = document.createElement('span'); lineNumberHTMLNode.className = 'sticky-line-number'; - lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`; + lineNumberHTMLNode.style.lineHeight = `${this._lineHeight}px`; const lineNumbersWidth = minimapSide === 'left' ? layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth : layoutInfo.contentLeft; lineNumberHTMLNode.style.width = `${lineNumbersWidth}px`; @@ -219,7 +220,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { innerLineNumberHTML.innerText = Math.abs(line - this._editor.getPosition()!.lineNumber).toString(); } innerLineNumberHTML.className = 'sticky-line-number-inner'; - innerLineNumberHTML.style.lineHeight = `${lineHeight}px`; + innerLineNumberHTML.style.lineHeight = `${this._lineHeight}px`; innerLineNumberHTML.style.width = `${layoutInfo.lineNumbersWidth}px`; innerLineNumberHTML.style.float = 'left'; if (minimapSide === 'left') { @@ -236,10 +237,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineHTMLNode.setAttribute(STICKY_LINE_INDEX_ATTR, String(index)); lineHTMLNode.tabIndex = 0; - lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`; - lineHTMLNode.style.lineHeight = `${lineHeight}px`; - lineNumberHTMLNode.style.height = `${lineHeight}px`; - lineHTMLNode.style.height = `${lineHeight}px`; + lineNumberHTMLNode.style.lineHeight = `${this._lineHeight}px`; + lineHTMLNode.style.lineHeight = `${this._lineHeight}px`; + lineNumberHTMLNode.style.height = `${this._lineHeight}px`; + lineHTMLNode.style.height = `${this._lineHeight}px`; // Special case for the last line of sticky scroll const isLastLine = index === this._lineNumbers.length - 1; @@ -249,8 +250,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; lineNumberHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; - const lastLineTop = `${index * lineHeight + this._lastLineRelativePosition}px`; - const intermediateLineTop = `${index * lineHeight}px`; + const lastLineTop = `${index * this._lineHeight + this._lastLineRelativePosition}px`; + const intermediateLineTop = `${index * this._lineHeight}px`; lineHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; @@ -278,8 +279,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { foldingIcon.className = ThemeIcon.asClassName(isRegionCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); - const lineHeight = this._editor.getOption(EditorOption.lineHeight); - let scrollTop = - lineHeight * index + 1; + let scrollTop = - this._lineHeight * index + 1; if (isRegionCollapsed) { scrollTop += this._editor.getTopForLineNumber(startLineNumber); } else { From 7b9d1179bee400f7cbf77c02832666e89c3ec371 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 09:32:15 +0200 Subject: [PATCH 194/607] rendering sticky scroll when line height and show folding controls changes --- .../contrib/stickyScroll/browser/stickyScrollController.ts | 7 ++++++- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 37647eccdf6..77f08ff3598 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -85,7 +85,12 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._readConfiguration(); const stickyScrollDomNode = this._stickyScrollWidget.getDomNode(); this._register(this._editor.onDidChangeConfiguration(e => { - if (e.hasChanged(EditorOption.stickyScroll) || e.hasChanged(EditorOption.minimap)) { + if ( + e.hasChanged(EditorOption.stickyScroll) + || e.hasChanged(EditorOption.minimap) + || e.hasChanged(EditorOption.lineHeight) + || e.hasChanged(EditorOption.showFoldingControls) + ) { this._readConfiguration(); } })); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index c84143d5bce..b42ebe7373e 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -264,7 +264,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): void { - if (!foldingModel) { + const showFoldingControls: 'mouseover' | 'always' | 'never' = this._editor.getOption(EditorOption.showFoldingControls); + if (!foldingModel || showFoldingControls === 'never') { return; } const foldingRegions = foldingModel.regions; From 1bffa0f1f905f0077968ffe53dc090a6fbec938e Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 09:44:51 +0200 Subject: [PATCH 195/607] using variables in order to define the values --- build/lib/stylelint/vscode-known-variables.json | 4 +++- src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css | 6 +++--- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 6133fe142b4..f8e63f83ab7 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -747,6 +747,8 @@ "--vscode-repl-line-height", "--vscode-sash-hover-size", "--vscode-sash-size", + "--vscode-editorStickyScroll-cursorOutsideHover", + "--vscode-editorStickyScroll-opacityOutsideHover", "--vscode-editorStickyScroll-scrollableWidth", "--window-border-color", "--workspace-trust-check-color", @@ -770,4 +772,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} \ No newline at end of file +} diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index ff9afd45bd0..8429a3892ff 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -37,13 +37,13 @@ width: 18px; height: 18px; float: right; - opacity: 0; - cursor: default; + cursor: var(--vscode-editorStickyScroll-cursorOutsideHover); + opacity: var(--vscode-editorStickyScroll-opacityOutsideHover); } .monaco-editor .sticky-line-number .codicon:hover { - opacity: 1; cursor: pointer; + opacity: 1; } .monaco-editor .sticky-line-content { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index b42ebe7373e..8e65e4176fc 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -268,6 +268,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!foldingModel || showFoldingControls === 'never') { return; } + container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', showFoldingControls === `mouseover` ? `default` : `pointer`); + container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', showFoldingControls === `mouseover` ? `0` : `1`); + const foldingRegions = foldingModel.regions; const indexOfFoldingRegion = foldingRegions.findRange(line); const startLineNumber = foldingRegions.getStartLineNumber(indexOfFoldingRegion); From 79506bb29f6248375d44c6faf39dd856fe04a7a2 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Aug 2023 10:22:03 +0200 Subject: [PATCH 196/607] voice - allowLoadingUnsignedLibraries for voice --- src/vs/platform/sharedProcess/electron-main/sharedProcess.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index c11fa7d0fc5..70059e1d7fc 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -19,6 +19,7 @@ import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtil import { parseSharedProcessDebugPort } from 'vs/platform/environment/node/environmentService'; import { assertIsDefined } from 'vs/base/common/types'; import { SharedProcessChannelConnection, SharedProcessRawConnection, SharedProcessLifecycle } from 'vs/platform/sharedProcess/common/sharedProcess'; +import { IProductService } from 'vs/platform/product/common/productService'; export class SharedProcess extends Disposable { @@ -34,6 +35,7 @@ export class SharedProcess extends Disposable { @ILogService private readonly logService: ILogService, @ILoggerMainService private readonly loggerMainService: ILoggerMainService, @IPolicyService private readonly policyService: IPolicyService, + @IProductService private readonly productService: IProductService ) { super(); @@ -162,7 +164,8 @@ export class SharedProcess extends Disposable { type: 'shared-process', entryPoint: 'vs/code/node/sharedProcess/sharedProcessMain', payload: this.createSharedProcessConfiguration(), - execArgv + execArgv, + allowLoadingUnsignedLibraries: !!process.env.VSCODE_VOICE_MODULE_PATH && this.productService.quality !== 'stable' // TODO@bpasero package }); } From a0ac773aca554cd715b9c04e7e2b26e2da388b75 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 25 Aug 2023 12:01:37 +0200 Subject: [PATCH 197/607] Git - fix issue with smart commit and dirty documents (#191300) --- extensions/git/src/commands.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index af40853e27e..704a4fa47fd 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1783,12 +1783,17 @@ export class CommandCenter { const message = documents.length === 1 ? l10n.t('The following file has unsaved changes which won\'t be included in the commit if you proceed: {0}.\n\nWould you like to save it before committing?', path.basename(documents[0].uri.fsPath)) : l10n.t('There are {0} unsaved files.\n\nWould you like to save them before committing?', documents.length); - const saveAndCommit = l10n.t('Save All & Commit'); - const commit = l10n.t('Commit Staged Changes'); + const saveAndCommit = l10n.t('Save All & Commit Changes'); + const commit = l10n.t('Commit Changes'); const pick = await window.showWarningMessage(message, { modal: true }, saveAndCommit, commit); if (pick === saveAndCommit) { await Promise.all(documents.map(d => d.save())); + + // After saving the dirty documents, if there are any documents that are part of the + // index group we have to add them back in order for the saved changes to be committed + documents = documents + .filter(d => repository.indexGroup.resourceStates.some(s => pathEquals(s.resourceUri.fsPath, d.uri.fsPath))); await repository.add(documents.map(d => d.uri)); noStagedChanges = repository.indexGroup.resourceStates.length === 0; From bb0a1b185b05e4158d8314876e3ae704ee779489 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 12:20:40 +0200 Subject: [PATCH 198/607] adding code in order to make all toggle icons appear on hover of left part --- .../lib/stylelint/vscode-known-variables.json | 1 + .../stickyScroll/browser/stickyScroll.css | 8 +-- .../browser/stickyScrollWidget.ts | 69 ++++++++++++++----- 3 files changed, 56 insertions(+), 22 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index f8e63f83ab7..bb3e1a501e9 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -749,6 +749,7 @@ "--vscode-sash-size", "--vscode-editorStickyScroll-cursorOutsideHover", "--vscode-editorStickyScroll-opacityOutsideHover", + "--vscode-editorStickyScroll-transitionOpacity", "--vscode-editorStickyScroll-scrollableWidth", "--window-border-color", "--workspace-trust-check-color", diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 8429a3892ff..ed7d615342a 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -37,14 +37,10 @@ width: 18px; height: 18px; float: right; - cursor: var(--vscode-editorStickyScroll-cursorOutsideHover); - opacity: var(--vscode-editorStickyScroll-opacityOutsideHover); + cursor: var(--vscode-editorStickyScroll-cursorOutsideHover, 'default'); + opacity: var(--vscode-editorStickyScroll-opacityOutsideHover, '0'); } -.monaco-editor .sticky-line-number .codicon:hover { - cursor: pointer; - opacity: 1; -} .monaco-editor .sticky-line-content { width: var(--vscode-editorStickyScroll-scrollableWidth); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 8e65e4176fc..050096cdd05 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -45,6 +45,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _lineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; private _minContentWidthInPx: number = 0; + private _collapsedLines: number[] = []; constructor( private readonly _editor: ICodeEditor @@ -147,9 +148,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const foldingModel = await FoldingController.get(this._editor)?.getFoldingModel(); const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { - const { lineNumberHTMLNode, renderedStickyLine } = this._renderChildNode(index, line, layoutInfo, foldingModel); - this._lineNumbersDomNode.appendChild(lineNumberHTMLNode); - this._linesDomNode.appendChild(renderedStickyLine.domNode); + const renderedStickyLine = this._renderChildNode(index, line, layoutInfo, foldingModel); + this._lineNumbersDomNode.appendChild(renderedStickyLine.lineNumberDomNode); + this._linesDomNode.appendChild(renderedStickyLine.lineDomNode); this._stickyLines.push(renderedStickyLine); } @@ -166,10 +167,38 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.style.marginLeft = '0px'; } this._updateMinContentWidth(); + this._setFoldingHoverListeners(); this._editor.layoutOverlayWidget(this); } - private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null | undefined): { lineNumberHTMLNode: HTMLSpanElement; renderedStickyLine: RenderedStickyLine } { + private _setFoldingHoverListeners(): void { + const showFoldingControls: 'mouseover' | 'always' | 'never' = this._editor.getOption(EditorOption.showFoldingControls); + if (showFoldingControls !== 'mouseover') { + return; + } + this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, () => { + for (const line of this._stickyLines) { + const lineNumberDomNode = line.lineNumberDomNode; + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', `pointer`); + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', `1`); + } + })); + this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_LEAVE, () => { + for (const line of this._stickyLines) { + const lineNumber = line.lineNumber; + const lineNumberDomNode = line.lineNumberDomNode; + if (this._collapsedLines.some(collapsedLine => collapsedLine === lineNumber)) { + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', `pointer`); + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', `1`); + } else { + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', showFoldingControls === `mouseover` ? `default` : `pointer`); + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', showFoldingControls === `mouseover` ? `0` : `1`); + } + } + })); + } + + private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null | undefined): RenderedStickyLine { const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); @@ -256,11 +285,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); - - return { - lineNumberHTMLNode, - renderedStickyLine: new RenderedStickyLine(line, lineHTMLNode, renderOutput.characterMapping) - }; + return new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, renderOutput.characterMapping); } private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): void { @@ -268,9 +293,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!foldingModel || showFoldingControls === 'never') { return; } - container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', showFoldingControls === `mouseover` ? `default` : `pointer`); - container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', showFoldingControls === `mouseover` ? `0` : `1`); - const foldingRegions = foldingModel.regions; const indexOfFoldingRegion = foldingRegions.findRange(line); const startLineNumber = foldingRegions.getStartLineNumber(indexOfFoldingRegion); @@ -281,12 +303,25 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const foldingIcon = container.appendChild(document.createElement('div')); const isRegionCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); foldingIcon.className = ThemeIcon.asClassName(isRegionCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); + + // Setting the folding style properties + if (this._collapsedLines.some(collapsedLine => collapsedLine === line)) { + container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', `pointer`); + container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', `1`); + } else { + container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', showFoldingControls === `mouseover` ? `default` : `pointer`); + container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', showFoldingControls === `mouseover` ? `0` : `1`); + } + + // Setting the click listener this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); let scrollTop = - this._lineHeight * index + 1; if (isRegionCollapsed) { + this._collapsedLines = this._collapsedLines.filter(line => line !== startLineNumber); scrollTop += this._editor.getTopForLineNumber(startLineNumber); } else { + this._collapsedLines.push(startLineNumber); const endLineNumber = foldingRegions.getEndLineNumber(indexOfFoldingRegion); scrollTop += this._editor.getTopForLineNumber(endLineNumber); } @@ -297,8 +332,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _updateMinContentWidth() { this._minContentWidthInPx = 0; for (const stickyLine of this._stickyLines) { - if (stickyLine.domNode.scrollWidth > this._minContentWidthInPx) { - this._minContentWidthInPx = stickyLine.domNode.scrollWidth; + if (stickyLine.lineDomNode.scrollWidth > this._minContentWidthInPx) { + this._minContentWidthInPx = stickyLine.lineDomNode.scrollWidth; } } this._minContentWidthInPx += this._editor.getLayoutInfo().verticalScrollbarWidth; @@ -324,7 +359,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { focusLineWithIndex(index: number) { if (0 <= index && index < this._stickyLines.length) { - this._stickyLines[index].domNode.focus(); + this._stickyLines[index].lineDomNode.focus(); } } @@ -374,8 +409,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { class RenderedStickyLine { constructor( + public readonly index: number, public readonly lineNumber: number, - public readonly domNode: HTMLElement, + public readonly lineDomNode: HTMLElement, + public readonly lineNumberDomNode: HTMLElement, public readonly characterMapping: CharacterMapping ) { } } From 4e1b2aeb4a5614f23b5ab7668447f95f58c0b861 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 13:01:53 +0200 Subject: [PATCH 199/607] adding opacity transition variable --- build/lib/stylelint/vscode-known-variables.json | 2 +- .../stickyScroll/browser/stickyScroll.css | 1 + .../stickyScroll/browser/stickyScrollWidget.ts | 17 ++++++++++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index bb3e1a501e9..af20ce988aa 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -749,7 +749,7 @@ "--vscode-sash-size", "--vscode-editorStickyScroll-cursorOutsideHover", "--vscode-editorStickyScroll-opacityOutsideHover", - "--vscode-editorStickyScroll-transitionOpacity", + "--vscode-editorStickyScroll-opacityTransition", "--vscode-editorStickyScroll-scrollableWidth", "--window-border-color", "--workspace-trust-check-color", diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index ed7d615342a..635713dd72c 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -39,6 +39,7 @@ float: right; cursor: var(--vscode-editorStickyScroll-cursorOutsideHover, 'default'); opacity: var(--vscode-editorStickyScroll-opacityOutsideHover, '0'); + transition: var(--vscode-editorStickyScroll-opacityTransition, 'opacity 0s'); } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 050096cdd05..768e3df004d 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -176,11 +176,25 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (showFoldingControls !== 'mouseover') { return; } - this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, () => { + this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, (e) => { + let insideOfStickyScroll = false; + if ('fromElement' in e && (e.fromElement instanceof HTMLElement)) { + if (e.fromElement.classList.contains('codicon')) { + insideOfStickyScroll = true; + } + } for (const line of this._stickyLines) { const lineNumberDomNode = line.lineNumberDomNode; + if (insideOfStickyScroll) { + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityTransition', 'opacity 0s'); + } lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', `pointer`); lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', `1`); + if (insideOfStickyScroll) { + setTimeout(() => { + lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityTransition', 'opacity 0.5s'); + }, 300); + } } })); this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_LEAVE, () => { @@ -312,6 +326,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', showFoldingControls === `mouseover` ? `default` : `pointer`); container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', showFoldingControls === `mouseover` ? `0` : `1`); } + container.style.setProperty('--vscode-editorStickyScroll-opacityTransition', 'opacity 0.5s'); // Setting the click listener this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { From d9c5ff85358b80c200e45f6b80a0dc1baa0432c3 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 13:48:39 +0200 Subject: [PATCH 200/607] cleaning the code --- .../browser/stickyScrollWidget.ts | 68 ++++++++----------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 768e3df004d..b699ecd995e 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -177,23 +177,19 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return; } this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, (e) => { - let insideOfStickyScroll = false; - if ('fromElement' in e && (e.fromElement instanceof HTMLElement)) { - if (e.fromElement.classList.contains('codicon')) { - insideOfStickyScroll = true; - } - } + const mouseEventTriggerredByClick = + 'fromElement' in e + && e.fromElement instanceof HTMLElement + && e.fromElement.classList.contains('codicon'); + for (const line of this._stickyLines) { const lineNumberDomNode = line.lineNumberDomNode; - if (insideOfStickyScroll) { - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityTransition', 'opacity 0s'); - } - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', `pointer`); - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', `1`); - if (insideOfStickyScroll) { - setTimeout(() => { - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityTransition', 'opacity 0.5s'); - }, 300); + if (mouseEventTriggerredByClick) { + this._setFoldingToggleIconTransitionRequired(lineNumberDomNode, false); + this._setFoldingToggleIconVisible(lineNumberDomNode, true); + setTimeout(() => { this._setFoldingToggleIconTransitionRequired(lineNumberDomNode, true); }, 300); + } else { + this._setFoldingToggleIconVisible(lineNumberDomNode, true); } } })); @@ -201,17 +197,21 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { for (const line of this._stickyLines) { const lineNumber = line.lineNumber; const lineNumberDomNode = line.lineNumberDomNode; - if (this._collapsedLines.some(collapsedLine => collapsedLine === lineNumber)) { - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', `pointer`); - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', `1`); - } else { - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', showFoldingControls === `mouseover` ? `default` : `pointer`); - lineNumberDomNode.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', showFoldingControls === `mouseover` ? `0` : `1`); - } + const isCollapsed = this._collapsedLines.some(collapsedLine => collapsedLine === lineNumber); + this._setFoldingToggleIconVisible(lineNumberDomNode, isCollapsed); } })); } + private _setFoldingToggleIconTransitionRequired(container: HTMLElement, requiresTrandition: boolean) { + container.style.setProperty('--vscode-editorStickyScroll-opacityTransition', `opacity ${requiresTrandition ? 0.5 : 0}s`); + } + + private _setFoldingToggleIconVisible(container: HTMLElement, visible: boolean) { + container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', visible ? 'pointer' : 'default'); + container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', visible ? '1' : '0'); + } + private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null | undefined): RenderedStickyLine { const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; @@ -299,7 +299,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); - return new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, renderOutput.characterMapping); + return new RenderedStickyLine(line, lineHTMLNode, lineNumberHTMLNode, renderOutput.characterMapping); } private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): void { @@ -317,29 +317,22 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const foldingIcon = container.appendChild(document.createElement('div')); const isRegionCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); foldingIcon.className = ThemeIcon.asClassName(isRegionCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); + const isCollapsed = this._collapsedLines.some(collapsedLine => collapsedLine === line); + this._setFoldingToggleIconVisible(container, isCollapsed || showFoldingControls === 'always'); + this._setFoldingToggleIconTransitionRequired(container, true); - // Setting the folding style properties - if (this._collapsedLines.some(collapsedLine => collapsedLine === line)) { - container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', `pointer`); - container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', `1`); - } else { - container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', showFoldingControls === `mouseover` ? `default` : `pointer`); - container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', showFoldingControls === `mouseover` ? `0` : `1`); - } - container.style.setProperty('--vscode-editorStickyScroll-opacityTransition', 'opacity 0.5s'); - - // Setting the click listener this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); - let scrollTop = - this._lineHeight * index + 1; + let scrollTop: number; if (isRegionCollapsed) { this._collapsedLines = this._collapsedLines.filter(line => line !== startLineNumber); - scrollTop += this._editor.getTopForLineNumber(startLineNumber); + scrollTop = this._editor.getTopForLineNumber(startLineNumber); } else { this._collapsedLines.push(startLineNumber); const endLineNumber = foldingRegions.getEndLineNumber(indexOfFoldingRegion); - scrollTop += this._editor.getTopForLineNumber(endLineNumber); + scrollTop = this._editor.getTopForLineNumber(endLineNumber); } + scrollTop += -this._lineHeight * index + 1; this._editor.setScrollTop(scrollTop); })); } @@ -424,7 +417,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { class RenderedStickyLine { constructor( - public readonly index: number, public readonly lineNumber: number, public readonly lineDomNode: HTMLElement, public readonly lineNumberDomNode: HTMLElement, From a016ebcfd6a14adf23266e8c4a0e7e902f2731b2 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 13:52:13 +0200 Subject: [PATCH 201/607] calling set folindg hover listeners when folding model is defined --- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index b699ecd995e..d335b97ee4e 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -153,6 +153,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._linesDomNode.appendChild(renderedStickyLine.lineDomNode); this._stickyLines.push(renderedStickyLine); } + if (foldingModel) { + this._setFoldingHoverListeners(); + } const widgetHeight: number = this._lineNumbers.length * this._lineHeight + this._lastLineRelativePosition; this._rootDomNode.style.display = widgetHeight > 0 ? 'block' : 'none'; @@ -167,7 +170,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.style.marginLeft = '0px'; } this._updateMinContentWidth(); - this._setFoldingHoverListeners(); this._editor.layoutOverlayWidget(this); } From 762c77d1ae6b63c82086658792d38e393736fb8c Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 14:07:44 +0200 Subject: [PATCH 202/607] cleaning the code --- .../browser/stickyScrollWidget.ts | 66 +++++++++++-------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index d335b97ee4e..311881d9ab6 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -149,7 +149,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { const renderedStickyLine = this._renderChildNode(index, line, layoutInfo, foldingModel); - this._lineNumbersDomNode.appendChild(renderedStickyLine.lineNumberDomNode); this._linesDomNode.appendChild(renderedStickyLine.lineDomNode); this._stickyLines.push(renderedStickyLine); } @@ -185,35 +184,27 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { && e.fromElement.classList.contains('codicon'); for (const line of this._stickyLines) { - const lineNumberDomNode = line.lineNumberDomNode; + const foldingIcon = line.foldingIcon; + if (!foldingIcon) { + continue; + } if (mouseEventTriggerredByClick) { - this._setFoldingToggleIconTransitionRequired(lineNumberDomNode, false); - this._setFoldingToggleIconVisible(lineNumberDomNode, true); - setTimeout(() => { this._setFoldingToggleIconTransitionRequired(lineNumberDomNode, true); }, 300); + foldingIcon.setTransitionRequired(false); + foldingIcon.setVisible(true); + setTimeout(() => { foldingIcon.setTransitionRequired(true); }, 300); } else { - this._setFoldingToggleIconVisible(lineNumberDomNode, true); + foldingIcon.setVisible(true); } } })); this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_LEAVE, () => { for (const line of this._stickyLines) { - const lineNumber = line.lineNumber; - const lineNumberDomNode = line.lineNumberDomNode; - const isCollapsed = this._collapsedLines.some(collapsedLine => collapsedLine === lineNumber); - this._setFoldingToggleIconVisible(lineNumberDomNode, isCollapsed); + const foldingIcon = line.foldingIcon; + foldingIcon?.setVisible(foldingIcon.isCollapsed); } })); } - private _setFoldingToggleIconTransitionRequired(container: HTMLElement, requiresTrandition: boolean) { - container.style.setProperty('--vscode-editorStickyScroll-opacityTransition', `opacity ${requiresTrandition ? 0.5 : 0}s`); - } - - private _setFoldingToggleIconVisible(container: HTMLElement, visible: boolean) { - container.style.setProperty('--vscode-editorStickyScroll-cursorOutsideHover', visible ? 'pointer' : 'default'); - container.style.setProperty('--vscode-editorStickyScroll-opacityOutsideHover', visible ? '1' : '0'); - } - private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null | undefined): RenderedStickyLine { const viewModel = this._editor._getViewModel(); const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; @@ -299,12 +290,11 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const intermediateLineTop = `${index * this._lineHeight}px`; lineHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; - - this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); - return new RenderedStickyLine(line, lineHTMLNode, lineNumberHTMLNode, renderOutput.characterMapping); + const foldingIcon = this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); + return new RenderedStickyLine(line, lineHTMLNode, foldingIcon, renderOutput.characterMapping); } - private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): void { + private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): FoldingIcon | undefined { const showFoldingControls: 'mouseover' | 'always' | 'never' = this._editor.getOption(EditorOption.showFoldingControls); if (!foldingModel || showFoldingControls === 'never') { return; @@ -316,14 +306,15 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!isFoldingScope) { return; } - const foldingIcon = container.appendChild(document.createElement('div')); + const foldingIconNode = container.appendChild(document.createElement('div')); const isRegionCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); - foldingIcon.className = ThemeIcon.asClassName(isRegionCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); + foldingIconNode.className = ThemeIcon.asClassName(isRegionCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); const isCollapsed = this._collapsedLines.some(collapsedLine => collapsedLine === line); - this._setFoldingToggleIconVisible(container, isCollapsed || showFoldingControls === 'always'); - this._setFoldingToggleIconTransitionRequired(container, true); + const foldingIcon = new FoldingIcon(foldingIconNode, isCollapsed); + foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); + foldingIcon.setTransitionRequired(false); - this._foldingIconStore.add(dom.addDisposableListener(foldingIcon, dom.EventType.CLICK, () => { + this._foldingIconStore.add(dom.addDisposableListener(foldingIconNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); let scrollTop: number; if (isRegionCollapsed) { @@ -337,6 +328,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { scrollTop += -this._lineHeight * index + 1; this._editor.setScrollTop(scrollTop); })); + return foldingIcon; } private _updateMinContentWidth() { @@ -421,7 +413,23 @@ class RenderedStickyLine { constructor( public readonly lineNumber: number, public readonly lineDomNode: HTMLElement, - public readonly lineNumberDomNode: HTMLElement, + public readonly foldingIcon: FoldingIcon | undefined, public readonly characterMapping: CharacterMapping ) { } } + +class FoldingIcon { + constructor( + private readonly foldingIconNode: HTMLElement, + public isCollapsed: boolean + ) { } + + public setVisible(visible: boolean) { + this.foldingIconNode.style.cursor = visible ? 'pointer' : 'default'; + this.foldingIconNode.style.opacity = visible ? '1' : '0'; + } + + public setTransitionRequired(transitionRequired: boolean) { + this.foldingIconNode.style.transition = `opacity ${transitionRequired ? 0.5 : 0}s`; + } +} From 676510544bcee41d88aaf9fd43c925ba63a31c74 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 14:10:59 +0200 Subject: [PATCH 203/607] cleaning the code --- build/lib/stylelint/vscode-known-variables.json | 5 +---- src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css | 3 --- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 6 ++++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index af20ce988aa..6133fe142b4 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -747,9 +747,6 @@ "--vscode-repl-line-height", "--vscode-sash-hover-size", "--vscode-sash-size", - "--vscode-editorStickyScroll-cursorOutsideHover", - "--vscode-editorStickyScroll-opacityOutsideHover", - "--vscode-editorStickyScroll-opacityTransition", "--vscode-editorStickyScroll-scrollableWidth", "--window-border-color", "--workspace-trust-check-color", @@ -773,4 +770,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} +} \ No newline at end of file diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 635713dd72c..bd4a74b9b76 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -37,9 +37,6 @@ width: 18px; height: 18px; float: right; - cursor: var(--vscode-editorStickyScroll-cursorOutsideHover, 'default'); - opacity: var(--vscode-editorStickyScroll-opacityOutsideHover, '0'); - transition: var(--vscode-editorStickyScroll-opacityTransition, 'opacity 0s'); } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 311881d9ab6..d887c25bdf4 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -150,6 +150,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { for (const [index, line] of this._lineNumbers.entries()) { const renderedStickyLine = this._renderChildNode(index, line, layoutInfo, foldingModel); this._linesDomNode.appendChild(renderedStickyLine.lineDomNode); + this._lineNumbersDomNode.appendChild(renderedStickyLine.lineNumberDomNode); this._stickyLines.push(renderedStickyLine); } if (foldingModel) { @@ -291,7 +292,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; const foldingIcon = this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); - return new RenderedStickyLine(line, lineHTMLNode, foldingIcon, renderOutput.characterMapping); + return new RenderedStickyLine(line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping); } private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): FoldingIcon | undefined { @@ -312,7 +313,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const isCollapsed = this._collapsedLines.some(collapsedLine => collapsedLine === line); const foldingIcon = new FoldingIcon(foldingIconNode, isCollapsed); foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); - foldingIcon.setTransitionRequired(false); + foldingIcon.setTransitionRequired(true); this._foldingIconStore.add(dom.addDisposableListener(foldingIconNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); @@ -413,6 +414,7 @@ class RenderedStickyLine { constructor( public readonly lineNumber: number, public readonly lineDomNode: HTMLElement, + public readonly lineNumberDomNode: HTMLElement, public readonly foldingIcon: FoldingIcon | undefined, public readonly characterMapping: CharacterMapping ) { } From 7e814d65705956f3deaf933fde3e850a61fce407 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 14:29:11 +0200 Subject: [PATCH 204/607] cleaning the code --- .../browser/stickyScrollWidget.ts | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index d887c25bdf4..a37050696a1 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -45,7 +45,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _lineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; private _minContentWidthInPx: number = 0; - private _collapsedLines: number[] = []; constructor( private readonly _editor: ICodeEditor @@ -308,25 +307,20 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return; } const foldingIconNode = container.appendChild(document.createElement('div')); - const isRegionCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); - foldingIconNode.className = ThemeIcon.asClassName(isRegionCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); - const isCollapsed = this._collapsedLines.some(collapsedLine => collapsedLine === line); + const isCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); + foldingIconNode.className = ThemeIcon.asClassName(isCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); const foldingIcon = new FoldingIcon(foldingIconNode, isCollapsed); foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); foldingIcon.setTransitionRequired(true); this._foldingIconStore.add(dom.addDisposableListener(foldingIconNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); - let scrollTop: number; - if (isRegionCollapsed) { - this._collapsedLines = this._collapsedLines.filter(line => line !== startLineNumber); - scrollTop = this._editor.getTopForLineNumber(startLineNumber); - } else { - this._collapsedLines.push(startLineNumber); - const endLineNumber = foldingRegions.getEndLineNumber(indexOfFoldingRegion); - scrollTop = this._editor.getTopForLineNumber(endLineNumber); - } - scrollTop += -this._lineHeight * index + 1; + foldingIcon.isCollapsed = !isCollapsed; + const scrollTop = + (isCollapsed ? + this._editor.getTopForLineNumber(startLineNumber) + : this._editor.getTopForLineNumber(foldingRegions.getEndLineNumber(indexOfFoldingRegion))) + - this._lineHeight * index + 1; this._editor.setScrollTop(scrollTop); })); return foldingIcon; From 85f166e05360f40ff9c97cbe5255375f0d428f39 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 25 Aug 2023 15:04:59 +0200 Subject: [PATCH 205/607] voice - use `LimitedQueue` to prevent parallel transcriptions (#191311) --- src/vs/base/common/async.ts | 116 +++++++++----- src/vs/base/test/common/async.test.ts | 150 +++++++++++------- .../sharedProcess/contrib/voiceTranscriber.ts | 25 ++- .../textfile/common/textFileEditorModel.ts | 36 ++--- .../common/storedFileWorkingCopy.ts | 36 ++--- 5 files changed, 215 insertions(+), 148 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 6b87b06b21a..152a6af7e4e 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -682,6 +682,31 @@ export class Queue extends Limiter { } } +/** + * Same as `Queue`, ensures that only 1 task is executed at the same time. The difference to `Queue` is that + * there is only 1 task about to be scheduled next. As such, calling `queue` while a task is executing will + * replace the currently queued task until it executes. + * + * As such, the returned promise may not be from the factory that is passed in but from the next factory that + * is running after having called `queue`. + */ +export class LimitedQueue { + + private readonly sequentializer = new TaskSequentializer(); + + private tasks = 0; + + queue(factory: ITask>): Promise { + if (!this.sequentializer.isRunning()) { + return this.sequentializer.run(this.tasks++, factory()); + } + + return this.sequentializer.queue(() => { + return this.sequentializer.run(this.tasks++, factory()); + }); + } +} + /** * A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource * by disposing them once the queue is empty. @@ -1267,83 +1292,92 @@ export async function retry(task: ITask>, delay: number, retries: //#region Task Sequentializer -interface IPendingTask { +interface IRunningTask { readonly taskId: number; readonly cancel: () => void; readonly promise: Promise; } -interface INextTask { +interface IQueuedTask { readonly promise: Promise; readonly promiseResolve: () => void; readonly promiseReject: (error: Error) => void; - run: () => Promise; + run: ITask>; } -export interface ITaskSequentializerWithPendingTask { - readonly pending: Promise; +export interface ITaskSequentializerWithRunningTask { + readonly running: Promise; } -export interface ITaskSequentializerWithNextTask { - readonly next: INextTask; +export interface ITaskSequentializerWithQueuedTask { + readonly queued: IQueuedTask; } +/** + * @deprecated use `LimitedQueue` instead for an easier to use API + */ export class TaskSequentializer { - private _pending?: IPendingTask; - private _next?: INextTask; + private _running?: IRunningTask; + private _queued?: IQueuedTask; - hasPending(taskId?: number): this is ITaskSequentializerWithPendingTask { + isRunning(taskId?: number): this is ITaskSequentializerWithRunningTask { if (typeof taskId === 'number') { - return this._pending?.taskId === taskId; + return this._running?.taskId === taskId; } - return !!this._pending; + return !!this._running; } - get pending(): Promise | undefined { - return this._pending?.promise; + get running(): Promise | undefined { + return this._running?.promise; } - cancelPending(): void { - this._pending?.cancel(); + cancelRunning(): void { + this._running?.cancel(); } - setPending(taskId: number, promise: Promise, onCancel?: () => void,): Promise { - this._pending = { taskId, cancel: () => onCancel?.(), promise }; + run(taskId: number, promise: Promise, onCancel?: () => void,): Promise { + this._running = { taskId, cancel: () => onCancel?.(), promise }; - promise.then(() => this.donePending(taskId), () => this.donePending(taskId)); + promise.then(() => this.doneRunning(taskId), () => this.doneRunning(taskId)); return promise; } - private donePending(taskId: number): void { - if (this._pending && taskId === this._pending.taskId) { + private doneRunning(taskId: number): void { + if (this._running && taskId === this._running.taskId) { - // only set pending to done if the promise finished that is associated with that taskId - this._pending = undefined; + // only set running to done if the promise finished that is associated with that taskId + this._running = undefined; - // schedule the next task now that we are free if we have any - this.triggerNext(); + // schedule the queued task now that we are free if we have any + this.runQueued(); } } - private triggerNext(): void { - if (this._next) { - const next = this._next; - this._next = undefined; + private runQueued(): void { + if (this._queued) { + const queued = this._queued; + this._queued = undefined; - // Run next task and complete on the associated promise - next.run().then(next.promiseResolve, next.promiseReject); + // Run queued task and complete on the associated promise + queued.run().then(queued.promiseResolve, queued.promiseReject); } } - setNext(run: () => Promise): Promise { + /** + * Note: the promise to schedule as next run MUST itself call `run`. + * Otherwise, this sequentializer will report `false` for `isRunning` + * even when this task is running. Missing this detail means that + * suddenly multiple tasks will run in parallel. + */ + queue(run: ITask>): Promise { - // this is our first next task, so we create associated promise with it + // this is our first queued task, so we create associated promise with it // so that we can return a promise that completes when the task has // completed. - if (!this._next) { + if (!this._queued) { let promiseResolve: () => void; let promiseReject: (error: Error) => void; const promise = new Promise((resolve, reject) => { @@ -1351,7 +1385,7 @@ export class TaskSequentializer { promiseReject = reject; }); - this._next = { + this._queued = { run, promise, promiseResolve: promiseResolve!, @@ -1359,20 +1393,20 @@ export class TaskSequentializer { }; } - // we have a previous next task, just overwrite it + // we have a previous queued task, just overwrite it else { - this._next.run = run; + this._queued.run = run; } - return this._next.promise; + return this._queued.promise; } - hasNext(): this is ITaskSequentializerWithNextTask { - return !!this._next; + hasQueued(): this is ITaskSequentializerWithQueuedTask { + return !!this._queued; } async join(): Promise { - return this._next?.promise ?? this._pending?.promise; + return this._queued?.promise ?? this._running?.promise; } } diff --git a/src/vs/base/test/common/async.test.ts b/src/vs/base/test/common/async.test.ts index 144c119389a..467708aa2c2 100644 --- a/src/vs/base/test/common/async.test.ts +++ b/src/vs/base/test/common/async.test.ts @@ -664,119 +664,119 @@ suite('Async', () => { }); suite('TaskSequentializer', () => { - test('pending basics', async function () { + test('execution basics', async function () { const sequentializer = new async.TaskSequentializer(); - assert.ok(!sequentializer.hasPending()); - assert.ok(!sequentializer.hasNext()); - assert.ok(!sequentializer.hasPending(2323)); - assert.ok(!sequentializer.pending); + assert.ok(!sequentializer.isRunning()); + assert.ok(!sequentializer.hasQueued()); + assert.ok(!sequentializer.isRunning(2323)); + assert.ok(!sequentializer.running); // pending removes itself after done - await sequentializer.setPending(1, Promise.resolve()); - assert.ok(!sequentializer.hasPending()); - assert.ok(!sequentializer.hasPending(1)); - assert.ok(!sequentializer.pending); - assert.ok(!sequentializer.hasNext()); + await sequentializer.run(1, Promise.resolve()); + assert.ok(!sequentializer.isRunning()); + assert.ok(!sequentializer.isRunning(1)); + assert.ok(!sequentializer.running); + assert.ok(!sequentializer.hasQueued()); // pending removes itself after done (use async.timeout) - sequentializer.setPending(2, async.timeout(1)); - assert.ok(sequentializer.hasPending()); - assert.ok(sequentializer.hasPending(2)); - assert.ok(!sequentializer.hasNext()); - assert.strictEqual(sequentializer.hasPending(1), false); - assert.ok(sequentializer.pending); + sequentializer.run(2, async.timeout(1)); + assert.ok(sequentializer.isRunning()); + assert.ok(sequentializer.isRunning(2)); + assert.ok(!sequentializer.hasQueued()); + assert.strictEqual(sequentializer.isRunning(1), false); + assert.ok(sequentializer.running); await async.timeout(2); - assert.strictEqual(sequentializer.hasPending(), false); - assert.strictEqual(sequentializer.hasPending(2), false); - assert.ok(!sequentializer.pending); + assert.strictEqual(sequentializer.isRunning(), false); + assert.strictEqual(sequentializer.isRunning(2), false); + assert.ok(!sequentializer.running); }); - test('pending and next (finishes instantly)', async function () { + test('executing and queued (finishes instantly)', async function () { const sequentializer = new async.TaskSequentializer(); let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; })); - // next finishes instantly - let nextDone = false; - const res = sequentializer.setNext(() => Promise.resolve(null).then(() => { nextDone = true; return; })); + // queued finishes instantly + let queuedDone = false; + const res = sequentializer.queue(() => Promise.resolve(null).then(() => { queuedDone = true; return; })); - assert.ok(sequentializer.hasNext()); + assert.ok(sequentializer.hasQueued()); await res; assert.ok(pendingDone); - assert.ok(nextDone); - assert.ok(!sequentializer.hasNext()); + assert.ok(queuedDone); + assert.ok(!sequentializer.hasQueued()); }); - test('pending and next (finishes after timeout)', async function () { + test('executing and queued (finishes after timeout)', async function () { const sequentializer = new async.TaskSequentializer(); let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; })); - // next finishes after async.timeout - let nextDone = false; - const res = sequentializer.setNext(() => async.timeout(1).then(() => { nextDone = true; return; })); + // queued finishes after async.timeout + let queuedDone = false; + const res = sequentializer.queue(() => async.timeout(1).then(() => { queuedDone = true; return; })); await res; assert.ok(pendingDone); - assert.ok(nextDone); - assert.ok(!sequentializer.hasNext()); + assert.ok(queuedDone); + assert.ok(!sequentializer.hasQueued()); }); - test('join (without next or pending)', async function () { + test('join (without executing or queued)', async function () { const sequentializer = new async.TaskSequentializer(); await sequentializer.join(); - assert.ok(!sequentializer.hasNext()); + assert.ok(!sequentializer.hasQueued()); }); - test('join (without next)', async function () { + test('join (without queued)', async function () { const sequentializer = new async.TaskSequentializer(); let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; })); await sequentializer.join(); assert.ok(pendingDone); - assert.ok(!sequentializer.hasPending()); + assert.ok(!sequentializer.isRunning()); }); - test('join (with next and pending)', async function () { + test('join (with executing and queued)', async function () { const sequentializer = new async.TaskSequentializer(); let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; })); - // next finishes after async.timeout - let nextDone = false; - sequentializer.setNext(() => async.timeout(1).then(() => { nextDone = true; return; })); + // queued finishes after async.timeout + let queuedDone = false; + sequentializer.queue(() => async.timeout(1).then(() => { queuedDone = true; return; })); await sequentializer.join(); assert.ok(pendingDone); - assert.ok(nextDone); - assert.ok(!sequentializer.hasPending()); - assert.ok(!sequentializer.hasNext()); + assert.ok(queuedDone); + assert.ok(!sequentializer.isRunning()); + assert.ok(!sequentializer.hasQueued()); }); - test('pending and multiple next (last one wins)', async function () { + test('executing and multiple queued (last one wins)', async function () { const sequentializer = new async.TaskSequentializer(); let pendingDone = false; - sequentializer.setPending(1, async.timeout(1).then(() => { pendingDone = true; return; })); + sequentializer.run(1, async.timeout(1).then(() => { pendingDone = true; return; })); - // next finishes after async.timeout + // queued finishes after async.timeout let firstDone = false; - const firstRes = sequentializer.setNext(() => async.timeout(2).then(() => { firstDone = true; return; })); + const firstRes = sequentializer.queue(() => async.timeout(2).then(() => { firstDone = true; return; })); let secondDone = false; - const secondRes = sequentializer.setNext(() => async.timeout(3).then(() => { secondDone = true; return; })); + const secondRes = sequentializer.queue(() => async.timeout(3).then(() => { secondDone = true; return; })); let thirdDone = false; - const thirdRes = sequentializer.setNext(() => async.timeout(4).then(() => { thirdDone = true; return; })); + const thirdRes = sequentializer.queue(() => async.timeout(4).then(() => { thirdDone = true; return; })); await Promise.all([firstRes, secondRes, thirdRes]); assert.ok(pendingDone); @@ -785,12 +785,12 @@ suite('Async', () => { assert.ok(thirdDone); }); - test('cancel pending', async function () { + test('cancel executing', async function () { const sequentializer = new async.TaskSequentializer(); let pendingCancelled = false; - sequentializer.setPending(1, async.timeout(1), () => pendingCancelled = true); - sequentializer.cancelPending(); + sequentializer.run(1, async.timeout(1), () => pendingCancelled = true); + sequentializer.cancelRunning(); assert.ok(pendingCancelled); }); @@ -1281,4 +1281,42 @@ suite('Async', () => { assert.strictEqual(worked, false); }); }); + + suite('LimitedQueue', () => { + + test('basics (with long running task)', async () => { + const limitedQueue = new async.LimitedQueue(); + + let counter = 0; + const promises = []; + for (let i = 0; i < 5; i++) { + promises.push(limitedQueue.queue(async () => { + counter = i; + await async.timeout(1); + })); + } + + await Promise.all(promises); + + // only the last task executed + assert.strictEqual(counter, 4); + }); + + test('basics (with sync running task)', async () => { + const limitedQueue = new async.LimitedQueue(); + + let counter = 0; + const promises = []; + for (let i = 0; i < 5; i++) { + promises.push(limitedQueue.queue(async () => { + counter = i; + })); + } + + await Promise.all(promises); + + // only the last task executed + assert.strictEqual(counter, 4); + }); + }); }); diff --git a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts index 210570ef952..11299d0848e 100644 --- a/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts +++ b/src/vs/code/node/sharedProcess/contrib/voiceTranscriber.ts @@ -9,7 +9,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IVoiceRecognitionService } from 'vs/platform/voiceRecognition/node/voiceRecognitionService'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { TaskSequentializer } from 'vs/base/common/async'; +import { LimitedQueue } from 'vs/base/common/async'; export class VoiceTranscriptionManager extends Disposable { @@ -34,9 +34,7 @@ class VoiceTranscriber extends Disposable { private static MAX_DATA_LENGTH = 30 /* seconds */ * 16000 /* sampling rate */ * 16 /* bith depth */ * 1 /* channels */ / 8; - private readonly transcriptionSequentializer = new TaskSequentializer(); - - private requests = 0; + private readonly transcriptionQueue = new LimitedQueue(); private data: Float32Array | undefined = undefined; @@ -67,7 +65,6 @@ class VoiceTranscriber extends Disposable { this.logService.info(`[voice] transcriber: closed connection`); cts.dispose(true); - this.transcriptionSequentializer.cancelPending(); }); } @@ -91,23 +88,21 @@ class VoiceTranscriber extends Disposable { } this.data = dataCandidate; - const data = this.data.slice(0); - this.requests++; - - if (!this.transcriptionSequentializer.hasPending()) { - this.transcriptionSequentializer.setPending(this.requests, this.transcribe(data, cancellation)); - } else { - this.transcriptionSequentializer.setNext(() => this.transcribe(data, cancellation)); - } + this.transcriptionQueue.queue(() => this.transcribe(cancellation)); } - private async transcribe(channelData: Float32Array, cancellation: CancellationToken): Promise { + private async transcribe(cancellation: CancellationToken): Promise { if (cancellation.isCancellationRequested) { return; } - const result = await this.voiceRecognitionService.transcribe(channelData, cancellation); + const data = this.data?.slice(0); + if (!data) { + return; + } + + const result = await this.voiceRecognitionService.transcribe(data, cancellation); if (cancellation.isCancellationRequested) { return; diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 51af0329394..a60dc9a4560 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -290,7 +290,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Unless there are explicit contents provided, it is important that we do not // resolve a model that is dirty or is in the process of saving to prevent data // loss. - if (!options?.contents && (this.dirty || this.saveSequentializer.hasPending())) { + if (!options?.contents && (this.dirty || this.saveSequentializer.isRunning())) { this.trace('resolve() - exit - without resolving because model is dirty or being saved'); return; @@ -767,15 +767,15 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil return; } - // Lookup any running pending save for this versionId and return it if found + // Lookup any running save for this versionId and return it if found // // Scenario: user invoked the save action multiple times quickly for the same contents // while the save was not yet finished to disk // - if (this.saveSequentializer.hasPending(versionId)) { - this.trace(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`); + if (this.saveSequentializer.isRunning(versionId)) { + this.trace(`doSave(${versionId}) - exit - found a running save for versionId ${versionId}`); - return this.saveSequentializer.pending; + return this.saveSequentializer.running; } // Return early if not dirty (unless forced) @@ -795,18 +795,18 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Scenario B: save is very slow (e.g. network share) and the user manages to change the buffer and trigger another save // while the first save has not returned yet. // - if (this.saveSequentializer.hasPending()) { + if (this.saveSequentializer.isRunning()) { this.trace(`doSave(${versionId}) - exit - because busy saving`); // Indicate to the save sequentializer that we want to - // cancel the pending operation so that ours can run - // before the pending one finishes. - // Currently this will try to cancel pending save - // participants but never a pending save. - this.saveSequentializer.cancelPending(); + // cancel the running operation so that ours can run + // before the running one finishes. + // Currently this will try to cancel running save + // participants but never a running save. + this.saveSequentializer.cancelRunning(); - // Register this as the next upcoming save and return - return this.saveSequentializer.setNext(() => this.doSave(options)); + // Queue this as the upcoming save and return + return this.saveSequentializer.queue(() => this.doSave(options)); } // Push all edit operations to the undo stack so that the user has a chance to @@ -817,7 +817,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil const saveCancellation = new CancellationTokenSource(); - return this.saveSequentializer.setPending(versionId, (async () => { + return this.saveSequentializer.run(versionId, (async () => { // A save participant can still change the model now and since we are so close to saving // we do not want to trigger another auto save or similar, so we block this @@ -894,13 +894,13 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil // Clear error flag since we are trying to save again this.inErrorMode = false; - // Save to Disk. We mark the save operation as currently pending with + // Save to Disk. We mark the save operation as currently running with // the latest versionId because it might have changed from a save // participant triggering this.trace(`doSave(${versionId}) - before write()`); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); const resolvedTextFileEditorModel = this; - return this.saveSequentializer.setPending(versionId, (async () => { + return this.saveSequentializer.run(versionId, (async () => { try { const stat = await this.textFileService.write(lastResolvedFileStat.resource, resolvedTextFileEditorModel.createSnapshot(), { mtime: lastResolvedFileStat.mtime, @@ -1013,14 +1013,14 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil case TextFileEditorModelState.ORPHAN: return this.inOrphanMode; case TextFileEditorModelState.PENDING_SAVE: - return this.saveSequentializer.hasPending(); + return this.saveSequentializer.isRunning(); case TextFileEditorModelState.SAVED: return !this.dirty; } } async joinState(state: TextFileEditorModelState.PENDING_SAVE): Promise { - return this.saveSequentializer.pending; + return this.saveSequentializer.running; } override getLanguageId(this: IResolvedTextFileEditorModel): string; diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts index d3fff96bb86..f96b3de0015 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts @@ -433,7 +433,7 @@ export class StoredFileWorkingCopy extend // Unless there are explicit contents provided, it is important that we do not // resolve a working copy that is dirty or is in the process of saving to prevent // data loss. - if (!options?.contents && (this.dirty || this.saveSequentializer.hasPending())) { + if (!options?.contents && (this.dirty || this.saveSequentializer.isRunning())) { this.trace('resolve() - exit - without resolving because file working copy is dirty or being saved'); return; @@ -851,15 +851,15 @@ export class StoredFileWorkingCopy extend return; } - // Lookup any running pending save for this versionId and return it if found + // Lookup any running save for this versionId and return it if found // // Scenario: user invoked the save action multiple times quickly for the same contents // while the save was not yet finished to disk // - if (this.saveSequentializer.hasPending(versionId)) { - this.trace(`doSave(${versionId}) - exit - found a pending save for versionId ${versionId}`); + if (this.saveSequentializer.isRunning(versionId)) { + this.trace(`doSave(${versionId}) - exit - found a running save for versionId ${versionId}`); - return this.saveSequentializer.pending; + return this.saveSequentializer.running; } // Return early if not dirty (unless forced) @@ -879,20 +879,20 @@ export class StoredFileWorkingCopy extend // Scenario B: save is very slow (e.g. network share) and the user manages to change the working copy and trigger another save // while the first save has not returned yet. // - if (this.saveSequentializer.hasPending()) { + if (this.saveSequentializer.isRunning()) { this.trace(`doSave(${versionId}) - exit - because busy saving`); // Indicate to the save sequentializer that we want to - // cancel the pending operation so that ours can run - // before the pending one finishes. - // Currently this will try to cancel pending save - // participants and pending snapshots from the + // cancel the running operation so that ours can run + // before the running one finishes. + // Currently this will try to cancel running save + // participants and running snapshots from the // save operation, but not the actual save which does // not support cancellation yet. - this.saveSequentializer.cancelPending(); + this.saveSequentializer.cancelRunning(); - // Register this as the next upcoming save and return - return this.saveSequentializer.setNext(() => this.doSave(options)); + // Queue this as the upcoming save and return + return this.saveSequentializer.queue(() => this.doSave(options)); } // Push all edit operations to the undo stack so that the user has a chance to @@ -903,7 +903,7 @@ export class StoredFileWorkingCopy extend const saveCancellation = new CancellationTokenSource(); - return this.saveSequentializer.setPending(versionId, (async () => { + return this.saveSequentializer.run(versionId, (async () => { // A save participant can still change the working copy now // and since we are so close to saving we do not want to trigger @@ -975,13 +975,13 @@ export class StoredFileWorkingCopy extend // Clear error flag since we are trying to save again this.inErrorMode = false; - // Save to Disk. We mark the save operation as currently pending with + // Save to Disk. We mark the save operation as currently running with // the latest versionId because it might have changed from a save // participant triggering this.trace(`doSave(${versionId}) - before write()`); const lastResolvedFileStat = assertIsDefined(this.lastResolvedFileStat); const resolvedFileWorkingCopy = this; - return this.saveSequentializer.setPending(versionId, (async () => { + return this.saveSequentializer.run(versionId, (async () => { try { const writeFileOptions: IWriteFileOptions = { mtime: lastResolvedFileStat.mtime, @@ -1256,14 +1256,14 @@ export class StoredFileWorkingCopy extend case StoredFileWorkingCopyState.ORPHAN: return this.isOrphaned(); case StoredFileWorkingCopyState.PENDING_SAVE: - return this.saveSequentializer.hasPending(); + return this.saveSequentializer.isRunning(); case StoredFileWorkingCopyState.SAVED: return !this.dirty; } } async joinState(state: StoredFileWorkingCopyState.PENDING_SAVE): Promise { - return this.saveSequentializer.pending; + return this.saveSequentializer.running; } //#endregion From bdb0f0d9558424f453d0b08cfc67aef8a8dd39b1 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 15:06:49 +0200 Subject: [PATCH 206/607] removing an additional space --- src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index bd4a74b9b76..f7dd169c1d7 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -39,7 +39,6 @@ float: right; } - .monaco-editor .sticky-line-content { width: var(--vscode-editorStickyScroll-scrollableWidth); background-color: inherit; From 6b74d08f5b021abbaeaed7113bae3d5aaea6081e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Fri, 25 Aug 2023 15:14:07 +0200 Subject: [PATCH 207/607] :lipstick: (#191313) cc @rzhao271 --- build/azure-pipelines/common/sign.js | 2 +- build/azure-pipelines/common/sign.ts | 2 +- build/azure-pipelines/linux/product-build-linux.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/azure-pipelines/common/sign.js b/build/azure-pipelines/common/sign.js index fc522d4ef60..993711adfbf 100644 --- a/build/azure-pipelines/common/sign.js +++ b/build/azure-pipelines/common/sign.js @@ -34,7 +34,7 @@ function getParams(type) { return '[{"keyCode":"CP-230012","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"Append","parameterValue":"/as"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-230012","operationSetCode":"SigntoolVerify","parameters":[{"parameterName":"VerifyAll","parameterValue":"/all"}],"toolName":"sign","toolVersion":"1.0"}]'; case 'windows-appx': return '[{"keyCode":"CP-229979","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-229979","operationSetCode":"SigntoolVerify","parameters":[],"toolName":"sign","toolVersion":"1.0"}]'; - case 'rpm': + case 'pgp': return '[{ "keyCode": "CP-450779-Pgp", "operationSetCode": "LinuxSign", "parameters": [], "toolName": "sign", "toolVersion": "1.0" }]'; case 'darwin-sign': return '[{"keyCode":"CP-401337-Apple","operationSetCode":"MacAppDeveloperSign","parameters":[{"parameterName":"Hardening","parameterValue":"--options=runtime"}],"toolName":"sign","toolVersion":"1.0"}]'; diff --git a/build/azure-pipelines/common/sign.ts b/build/azure-pipelines/common/sign.ts index 955c9389c02..494e89b3e12 100644 --- a/build/azure-pipelines/common/sign.ts +++ b/build/azure-pipelines/common/sign.ts @@ -35,7 +35,7 @@ function getParams(type: string): string { return '[{"keyCode":"CP-230012","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"Append","parameterValue":"/as"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-230012","operationSetCode":"SigntoolVerify","parameters":[{"parameterName":"VerifyAll","parameterValue":"/all"}],"toolName":"sign","toolVersion":"1.0"}]'; case 'windows-appx': return '[{"keyCode":"CP-229979","operationSetCode":"SigntoolSign","parameters":[{"parameterName":"OpusName","parameterValue":"VS Code"},{"parameterName":"OpusInfo","parameterValue":"https://code.visualstudio.com/"},{"parameterName":"FileDigest","parameterValue":"/fd \\"SHA256\\""},{"parameterName":"PageHash","parameterValue":"/NPH"},{"parameterName":"TimeStamp","parameterValue":"/tr \\"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\\" /td sha256"}],"toolName":"sign","toolVersion":"1.0"},{"keyCode":"CP-229979","operationSetCode":"SigntoolVerify","parameters":[],"toolName":"sign","toolVersion":"1.0"}]'; - case 'rpm': + case 'pgp': return '[{ "keyCode": "CP-450779-Pgp", "operationSetCode": "LinuxSign", "parameters": [], "toolName": "sign", "toolVersion": "1.0" }]'; case 'darwin-sign': return '[{"keyCode":"CP-401337-Apple","operationSetCode":"MacAppDeveloperSign","parameters":[{"parameterName":"Hardening","parameterValue":"--options=runtime"}],"toolName":"sign","toolVersion":"1.0"}]'; diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 4641edf7670..919d48eac37 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -312,10 +312,10 @@ steps: continueOnError: true displayName: Download ESRPClient - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/deb '*.deb' + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/deb '*.deb' displayName: Codesign deb - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' + - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' displayName: Codesign rpm - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" From f430cf739885b20cfe9a34cdc424af72a644b8c0 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 15:17:53 +0200 Subject: [PATCH 208/607] setting the width and height inside of render function --- src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css | 2 -- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index f7dd169c1d7..212fbb7a050 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -34,8 +34,6 @@ } .monaco-editor .sticky-line-number .codicon { - width: 18px; - height: 18px; float: right; } diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index a37050696a1..0ea7ff20074 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -307,6 +307,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return; } const foldingIconNode = container.appendChild(document.createElement('div')); + foldingIconNode.style.width = `${this._lineHeight}px`; + foldingIconNode.style.height = `${this._lineHeight}px`; const isCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); foldingIconNode.className = ThemeIcon.asClassName(isCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); const foldingIcon = new FoldingIcon(foldingIconNode, isCollapsed); From fb73ec43a44c5cdf47111cffdadf637af60f6755 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 15:21:00 +0200 Subject: [PATCH 209/607] changing last line top when collapsed --- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 0ea7ff20074..637bb0774df 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -265,6 +265,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { innerLineNumberHTML.style.paddingLeft = `${layoutInfo.lineNumbersLeft}px`; } lineNumberHTMLNode.appendChild(innerLineNumberHTML); + const foldingIcon = this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); this._editor.applyFontInfo(lineHTMLNode); this._editor.applyFontInfo(innerLineNumberHTML); @@ -286,11 +287,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; lineNumberHTMLNode.style.zIndex = isLastLine ? lastLineZIndex : intermediateLineZIndex; - const lastLineTop = `${index * this._lineHeight + this._lastLineRelativePosition}px`; + const lastLineTop = `${index * this._lineHeight + this._lastLineRelativePosition + (foldingIcon?.isCollapsed ? 1 : 0)}px`; const intermediateLineTop = `${index * this._lineHeight}px`; lineHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; lineNumberHTMLNode.style.top = isLastLine ? lastLineTop : intermediateLineTop; - const foldingIcon = this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); return new RenderedStickyLine(line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping); } From e6d5e35f20f25407bddaba24841e2296be15f13c Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 15:29:33 +0200 Subject: [PATCH 210/607] placing code which generates the folding icon into the class --- .../browser/stickyScrollWidget.ts | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 637bb0774df..610eff4fb8c 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -294,7 +294,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return new RenderedStickyLine(line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping); } - private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): FoldingIcon | undefined { + private _renderFoldingIconForLine(container: HTMLSpanElement, foldingModel: FoldingModel | null | undefined, index: number, line: number): StickyFoldingIcon | undefined { const showFoldingControls: 'mouseover' | 'always' | 'never' = this._editor.getOption(EditorOption.showFoldingControls); if (!foldingModel || showFoldingControls === 'never') { return; @@ -306,16 +306,13 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { if (!isFoldingScope) { return; } - const foldingIconNode = container.appendChild(document.createElement('div')); - foldingIconNode.style.width = `${this._lineHeight}px`; - foldingIconNode.style.height = `${this._lineHeight}px`; const isCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); - foldingIconNode.className = ThemeIcon.asClassName(isCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); - const foldingIcon = new FoldingIcon(foldingIconNode, isCollapsed); + const foldingIcon = new StickyFoldingIcon(isCollapsed, this._lineHeight); + container.append(foldingIcon.domNode); foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); foldingIcon.setTransitionRequired(true); - this._foldingIconStore.add(dom.addDisposableListener(foldingIconNode, dom.EventType.CLICK, () => { + this._foldingIconStore.add(dom.addDisposableListener(foldingIcon.domNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); foldingIcon.isCollapsed = !isCollapsed; const scrollTop = @@ -411,23 +408,31 @@ class RenderedStickyLine { public readonly lineNumber: number, public readonly lineDomNode: HTMLElement, public readonly lineNumberDomNode: HTMLElement, - public readonly foldingIcon: FoldingIcon | undefined, + public readonly foldingIcon: StickyFoldingIcon | undefined, public readonly characterMapping: CharacterMapping ) { } } -class FoldingIcon { +class StickyFoldingIcon { + + public domNode: HTMLElement; + constructor( - private readonly foldingIconNode: HTMLElement, - public isCollapsed: boolean - ) { } + public isCollapsed: boolean, + public dimension: number + ) { + this.domNode = document.createElement('div'); + this.domNode.style.width = `${dimension}px`; + this.domNode.style.height = `${dimension}px`; + this.domNode.className = ThemeIcon.asClassName(isCollapsed ? foldingCollapsedIcon : foldingExpandedIcon); + } public setVisible(visible: boolean) { - this.foldingIconNode.style.cursor = visible ? 'pointer' : 'default'; - this.foldingIconNode.style.opacity = visible ? '1' : '0'; + this.domNode.style.cursor = visible ? 'pointer' : 'default'; + this.domNode.style.opacity = visible ? '1' : '0'; } public setTransitionRequired(transitionRequired: boolean) { - this.foldingIconNode.style.transition = `opacity ${transitionRequired ? 0.5 : 0}s`; + this.domNode.style.transition = `opacity ${transitionRequired ? 0.5 : 0}s`; } } From 0a3bb7ef2db3b52f6c88d864e1c000e3acff3604 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 15:41:08 +0200 Subject: [PATCH 211/607] cleaning the code --- .../editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 15899c9a054..00496bec0f0 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -109,9 +109,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return this._lineNumbers; } - setState(state: StickyScrollWidgetState): void { + setState(state: StickyScrollWidgetState | undefined): void { this._clearStickyWidget(); - if (!state) { + if (!state || !this._editor._getViewModel()) { return; } const futureWidgetHeight = state.startLineNumbers.length * this._lineHeight + state.lastLineRelativePosition; From d6d666effe991a730c67dcc758d3a1dcb541ca2e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 06:52:48 -0700 Subject: [PATCH 212/607] Correct dim unfocused config scope Part of #190751 --- .../accessibility/browser/accessibilityConfiguration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index c8272bc512d..8dbcbd3abff 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -111,7 +111,7 @@ export function registerAccessibilityConfiguration() { type: 'boolean', default: false, tags: ['accessibility'], - scope: ConfigurationScope.MACHINE, + scope: ConfigurationScope.APPLICATION, }, [AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity]: { description: localize('dimUnfocusedOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.', `\`#${AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled}#\``), @@ -120,7 +120,7 @@ export function registerAccessibilityConfiguration() { maximum: ViewDimUnfocusedOpacityProperties.Maximum, default: ViewDimUnfocusedOpacityProperties.Default, tags: ['accessibility'], - scope: ConfigurationScope.MACHINE, + scope: ConfigurationScope.APPLICATION, } } }); From 057182e86734e506c86b3805a08252c79e2fddf1 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 16:10:58 +0200 Subject: [PATCH 213/607] retriggering the tests --- src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 00496bec0f0..d8a3642eeb1 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -320,7 +320,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { container.append(foldingIcon.domNode); foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); foldingIcon.setTransitionRequired(true); - this._foldingIconStore.add(dom.addDisposableListener(foldingIcon.domNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); foldingIcon.isCollapsed = !isCollapsed; From 56b48db9e81d1d587f4c6e136e6399e35c29fdfd Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 25 Aug 2023 16:40:38 +0200 Subject: [PATCH 214/607] retriggering the tests once again --- src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index d8a3642eeb1..00496bec0f0 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -320,6 +320,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { container.append(foldingIcon.domNode); foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); foldingIcon.setTransitionRequired(true); + this._foldingIconStore.add(dom.addDisposableListener(foldingIcon.domNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); foldingIcon.isCollapsed = !isCollapsed; From 65e6594a6725dae0f68b05cff94f6117ebff6fbd Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 25 Aug 2023 08:41:54 -0700 Subject: [PATCH 215/607] rm command, handle hiding accessible buffer in that class --- .../terminal/browser/terminalActions.ts | 5 +++++ .../contrib/terminal/common/terminal.ts | 2 -- .../terminal.accessibility.contribution.ts | 20 +------------------ .../browser/terminalAccessibleBuffer.ts | 1 + .../browser/terminalAccessibleWidget.ts | 8 +++++--- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 6f0a337f55f..8e58f90c8fc 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -478,6 +478,11 @@ export function registerTerminalActions() { registerTerminalAction({ id: TerminalCommandId.Focus, title: terminalStrings.focus, + keybinding: { + when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.accessibleBufferOnLastLine), + primary: KeyMod.CtrlCmd | KeyCode.DownArrow, + weight: KeybindingWeight.WorkbenchContrib + }, precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), run: async (c) => { const instance = c.service.activeInstance || await c.service.createTerminal({ location: TerminalLocation.Panel }); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 46692bccd1b..77e37c5b574 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -495,7 +495,6 @@ export const enum TerminalCommandId { HideSuggestWidget = 'workbench.action.terminal.hideSuggestWidget', FocusHover = 'workbench.action.terminal.focusHover', ShowEnvironmentContributions = 'workbench.action.terminal.showEnvironmentContributions', - FocusAndHideAccessibleBuffer = 'workbench.action.terminal.focusAndHideAccessibleBuffer', // Developer commands @@ -570,7 +569,6 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TerminalCommandId.HideSuggestWidget, TerminalCommandId.FocusHover, AccessibilityCommandId.OpenAccessibilityHelp, - TerminalCommandId.FocusAndHideAccessibleBuffer, 'editor.action.toggleTabFocusMode', 'notifications.hideList', 'notifications.hideToasts', diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 96b9602e45d..7fd8369f2ea 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -11,7 +11,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { TerminalLocation, terminalTabFocusModeContextKey } from 'vs/platform/terminal/common/terminal'; +import { terminalTabFocusModeContextKey } from 'vs/platform/terminal/common/terminal'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -20,7 +20,6 @@ import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/brow import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { TerminalAccessibleContentProvider } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp'; import { AccessibleBufferWidget, NavigationType } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer'; import { TextAreaSyncAddon } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon'; @@ -207,20 +206,3 @@ registerTerminalAction({ await AccessibleBufferContribution.get(instance)?.navigateToCommand(NavigationType.Previous); } }); - -registerTerminalAction({ - id: TerminalCommandId.FocusAndHideAccessibleBuffer, - title: terminalStrings.focusAndHideAccessibleBuffer, - f1: false, - keybinding: { - when: ContextKeyExpr.and(TerminalContextKeys.accessibleBufferFocus, TerminalContextKeys.accessibleBufferOnLastLine), - primary: KeyMod.CtrlCmd | KeyCode.DownArrow, - weight: KeybindingWeight.WorkbenchContrib - }, - precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - run: async (c) => { - const instance = c.service.activeInstance || await c.service.createTerminal({ location: TerminalLocation.Panel }); - instance.getContribution(AccessibleBufferContribution.ID)?.hide(); - instance.focus(true); - } -}); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts index b851ad094ca..41992fae298 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts @@ -60,6 +60,7 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { super(ClassName.AccessibleBuffer, _instance, _xterm, TerminalContextKeys.accessibleBufferFocus, TerminalContextKeys.accessibleBufferOnLastLine, _instantiationService, _modelService, _configurationService, _contextKeyService, _terminalService); this._bufferTracker = _instantiationService.createInstance(BufferContentTracker, _xterm); this.element.ariaRoleDescription = localize('terminal.integrated.accessibleBuffer', 'Terminal buffer'); + _instance.onDidRequestFocus(() => this.hide(true)); this.updateEditor(); this.add(this.editorWidget.onDidFocusEditorText(async () => { if (this.element.classList.contains(ClassName.Active)) { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts index b95d8814f1e..6b43d58d553 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts @@ -116,8 +116,7 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { switch (e.keyCode) { case KeyCode.Escape: // On escape, hide the accessible buffer and force focus onto the terminal - this.hide(); - this._xterm.raw.focus(); + this.hide(true); break; case KeyCode.Tab: // On tab or shift+tab, hide the accessible buffer and perform the default tab @@ -163,10 +162,13 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { } } - hide(): void { + hide(focusXterm?: boolean): void { this._disposeListeners(); this.element.classList.remove(ClassName.Active); this._xtermElement.classList.remove(ClassName.Hide); + if (focusXterm) { + this._xterm.raw.focus(); + } } async getTextModel(resource: URI): Promise { From df978b63cbb94405fc2a6ac5a466340c041f34d9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 25 Aug 2023 09:02:32 -0700 Subject: [PATCH 216/607] fix #191315 --- .../browser/accessibilityConfiguration.ts | 14 ++++++++++++++ .../accessibility/browser/accessibleView.ts | 1 - .../accessibility/browser/accessibleViewActions.ts | 6 +++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index c8272bc512d..98e96695a64 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -45,6 +45,20 @@ export const enum AccessibilityVerbositySettingId { EditorUntitledHint = 'accessibility.verbosity.editor.untitledHint' } +export const enum AccessibleViewProviderId { + Terminal = 'terminal', + DiffEditor = 'diffEditor', + Chat = 'panelChat', + InlineChat = 'inlineChat', + InlineCompletions = 'inlineCompletions', + KeybindingsEditor = 'keybindingsEditor', + Notebook = 'notebook', + Editor = 'editor', + Hover = 'hover', + Notification = 'notification', + EditorUntitledHint = 'editor.untitledHint' +} + const baseProperty: object = { type: 'boolean', default: true, diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 02c1f17b6f6..d2d79a27377 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -403,7 +403,6 @@ class AccessibleView extends Disposable { } private _updateToolbar(providedActions?: IAction[], type?: AccessibleViewType): void { - this._toolbar.setActions([]); this._toolbar.setAriaLabel(type === AccessibleViewType.Help ? localize('accessibleHelpToolbar', 'Accessibility Help') : localize('accessibleViewToolbar', "Accessible View")); const menuActions: IAction[] = []; const toolbarMenu = this._register(this._menuService.createMenu(MenuId.AccessibleView, this._contextKeyService)); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index 9133dc3fd82..6c3034b4b05 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -11,7 +11,7 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/common/accessibilityCommands'; -import { AccessibilityVerbositySettingId, accessibilityHelpIsShown, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { AccessibleViewProviderId, accessibilityHelpIsShown, accessibleViewCurrentProviderId, accessibleViewGoToSymbolSupported, accessibleViewIsShown, accessibleViewSupportsNavigation, accessibleViewVerbosityEnabled } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; @@ -182,7 +182,7 @@ class AccessibleViewAcceptInlineCompletionAction extends Action2 { constructor() { super({ id: AccessibilityCommandId.AccessibleViewAcceptInlineCompletion, - precondition: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibilityVerbositySettingId.InlineCompletions)), + precondition: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.InlineCompletions)), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.Slash, mac: { primary: KeyMod.WinCtrl | KeyCode.Slash }, @@ -195,7 +195,7 @@ class AccessibleViewAcceptInlineCompletionAction extends Action2 { id: MenuId.AccessibleView, group: 'navigation', order: 0, - when: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibilityVerbositySettingId.InlineCompletions)) + when: ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.InlineCompletions)) }], title: localize('editor.action.accessibleViewAcceptInlineCompletionAction', "Accept Inline Completion") }); From 44e9c6d91db0df94e2e802eb7fae9adcaadbec10 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 25 Aug 2023 09:16:07 -0700 Subject: [PATCH 217/607] fix #187859 --- src/vs/editor/common/standaloneStrings.ts | 2 ++ .../accessibility/browser/accessibilityContributions.ts | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index f2251207711..87a6d066017 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -20,6 +20,8 @@ export namespace AccessibilityHelpNLS { export const screenReaderModeDisabled = nls.localize("screenReaderModeDisabled", "Screen Reader Optimized Mode disabled."); export const tabFocusModeOnMsg = nls.localize("tabFocusModeOnMsg", "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."); export const tabFocusModeOnMsgNoKb = nls.localize("tabFocusModeOnMsgNoKb", "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."); + export const stickScrollKb = nls.localize("stickScrollKb", "Run the command: Focus Sticky Scroll ({0}) to focus the currently nested scopes"); + export const stickScrollNoKb = nls.localize("stickScrollNoKb", "Run the command: Focus Sticky Scroll to focus the currently nested scopes. It is currently not triggerable by a keybinding."); export const tabFocusModeOffMsg = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."); export const tabFocusModeOffMsgNoKb = nls.localize("tabFocusModeOffMsgNoKb", "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding."); export const showAccessibilityHelpAction = nls.localize("showAccessibilityHelpAction", "Show Accessibility Help"); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index cfab22c7cc3..720720b02df 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -96,6 +96,10 @@ class AccessibilityHelpProvider implements IAccessibleContentProvider { } } + if (options.get(EditorOption.stickyScroll)) { + content.push(this._descriptionForCommand('editor.action.focusStickyScroll', AccessibilityHelpNLS.stickScrollKb, AccessibilityHelpNLS.stickScrollNoKb)); + } + if (options.get(EditorOption.tabFocusMode)) { content.push(this._descriptionForCommand(ToggleTabFocusModeAction.ID, AccessibilityHelpNLS.tabFocusModeOnMsg, AccessibilityHelpNLS.tabFocusModeOnMsgNoKb)); } else { From ca22267ddc39887b20b55cc968711272ef25f499 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:21:03 -0700 Subject: [PATCH 218/607] Keep both capability event types --- .../common/capabilities/capabilities.ts | 18 +++++++------- .../capabilities/terminalCapabilityStore.ts | 24 +++++++++---------- .../terminal/browser/terminalEditorService.ts | 4 ++-- .../contrib/terminal/browser/terminalGroup.ts | 4 ++-- .../terminal/browser/terminalInstance.ts | 6 ++--- .../terminal/browser/terminalService.ts | 4 ++-- .../contrib/terminal/browser/terminalView.ts | 2 +- .../terminal/browser/xterm/decorationAddon.ts | 4 ++-- .../terminalCapabilityStore.test.ts | 8 +++---- .../quickFix/browser/quickFixAddon.ts | 2 +- 10 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index c8fd3e5f85a..926b1cdb7ce 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -83,26 +83,28 @@ export interface ITerminalCapabilityStore { readonly items: IterableIterator; /** - * Fired when a capability is added. - * @deprecated Use onDidAddCapability2 + * Fired when a capability is added. The event data for this is only the + * {@link TerminalCapability} type, use {@link onDidAddCapability} to access the actual + * capability. */ - readonly onDidAddCapability: Event; + readonly onDidAddCapabilityType: Event; /** - * Fired when a capability is removed. - * @deprecated Use onDidRemoveCapability2 + * Fired when a capability is removed. The event data for this is only the + * {@link TerminalCapability} type, use {@link onDidAddCapability} to access the actual + * capability. */ - readonly onDidRemoveCapability: Event; + readonly onDidRemoveCapabilityType: Event; /** * Fired when a capability is added. */ - readonly onDidAddCapability2: Event>; + readonly onDidAddCapability: Event>; /** * Fired when a capability is removed. */ - readonly onDidRemoveCapability2: Event>; + readonly onDidRemoveCapability: Event>; /** * Gets whether the capability exists in the store. diff --git a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts index bdc3b4fdb50..2589c2c7f5c 100644 --- a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts +++ b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts @@ -11,14 +11,14 @@ export class TerminalCapabilityStore extends Disposable implements ITerminalCapa private _map: Map = new Map(); private readonly _onDidRemoveCapability = this._register(new Emitter()); - readonly onDidRemoveCapability = this._onDidRemoveCapability.event; + readonly onDidRemoveCapabilityType = this._onDidRemoveCapability.event; private readonly _onDidAddCapability = this._register(new Emitter()); - readonly onDidAddCapability = this._onDidAddCapability.event; + readonly onDidAddCapabilityType = this._onDidAddCapability.event; private readonly _onDidRemoveCapability2 = this._register(new Emitter>()); - readonly onDidRemoveCapability2 = this._onDidRemoveCapability2.event; + readonly onDidRemoveCapability = this._onDidRemoveCapability2.event; private readonly _onDidAddCapability2 = this._register(new Emitter>()); - readonly onDidAddCapability2 = this._onDidAddCapability2.event; + readonly onDidAddCapability = this._onDidAddCapability2.event; get items(): IterableIterator { return this._map.keys(); @@ -54,14 +54,14 @@ export class TerminalCapabilityStoreMultiplexer extends Disposable implements IT readonly _stores: ITerminalCapabilityStore[] = []; private readonly _onDidRemoveCapability = this._register(new Emitter()); - readonly onDidRemoveCapability = this._onDidRemoveCapability.event; + readonly onDidRemoveCapabilityType = this._onDidRemoveCapability.event; private readonly _onDidAddCapability = this._register(new Emitter()); - readonly onDidAddCapability = this._onDidAddCapability.event; + readonly onDidAddCapabilityType = this._onDidAddCapability.event; private readonly _onDidRemoveCapability2 = this._register(new Emitter>()); - readonly onDidRemoveCapability2 = this._onDidRemoveCapability2.event; + readonly onDidRemoveCapability = this._onDidRemoveCapability2.event; private readonly _onDidAddCapability2 = this._register(new Emitter>()); - readonly onDidAddCapability2 = this._onDidAddCapability2.event; + readonly onDidAddCapability = this._onDidAddCapability2.event; get items(): IterableIterator { return this._items(); @@ -102,9 +102,9 @@ export class TerminalCapabilityStoreMultiplexer extends Disposable implements IT this._onDidAddCapability.fire(capability); this._onDidAddCapability2.fire({ id: capability, capability: store.get(capability)! }); } - store.onDidAddCapability(e => this._onDidAddCapability.fire(e)); - store.onDidAddCapability2(e => this._onDidAddCapability2.fire(e)); - store.onDidRemoveCapability(e => this._onDidRemoveCapability.fire(e)); - store.onDidRemoveCapability2(e => this._onDidRemoveCapability2.fire(e)); + store.onDidAddCapabilityType(e => this._onDidAddCapability.fire(e)); + store.onDidAddCapability(e => this._onDidAddCapability2.fire(e)); + store.onDidRemoveCapabilityType(e => this._onDidRemoveCapability.fire(e)); + store.onDidRemoveCapability(e => this._onDidRemoveCapability2.fire(e)); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts index 50d3e14ba2a..bc78184a8d9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEditorService.ts @@ -177,8 +177,8 @@ export class TerminalEditorService extends Disposable implements ITerminalEditor this._instanceDisposables.set(inputKey, [ instance.onDidFocus(this._onDidFocusInstance.fire, this._onDidFocusInstance), instance.onDisposed(this._onDidDisposeInstance.fire, this._onDidDisposeInstance), - instance.capabilities.onDidAddCapability(() => this._onDidChangeInstanceCapability.fire(instance)), - instance.capabilities.onDidRemoveCapability(() => this._onDidChangeInstanceCapability.fire(instance)), + instance.capabilities.onDidAddCapabilityType(() => this._onDidChangeInstanceCapability.fire(instance)), + instance.capabilities.onDidRemoveCapabilityType(() => this._onDidChangeInstanceCapability.fire(instance)), ]); this.instances.push(instance); this._onDidChangeInstances.fire(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index f786225c38d..bf6dc33b430 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -369,8 +369,8 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { this._setActiveInstance(instance); this._onDidFocusInstance.fire(instance); }), - instance.capabilities.onDidAddCapability(() => this._onDidChangeInstanceCapability.fire(instance)), - instance.capabilities.onDidRemoveCapability(() => this._onDidChangeInstanceCapability.fire(instance)), + instance.capabilities.onDidAddCapabilityType(() => this._onDidChangeInstanceCapability.fire(instance)), + instance.capabilities.onDidRemoveCapabilityType(() => this._onDidChangeInstanceCapability.fire(instance)), ]); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index a699b24c929..3262f52d621 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -412,7 +412,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._terminalShellIntegrationEnabledContextKey = TerminalContextKeys.terminalShellIntegrationEnabled.bindTo(scopedContextKeyService); this._logService.trace(`terminalInstance#ctor (instanceId: ${this.instanceId})`, this._shellLaunchConfig); - this._register(this.capabilities.onDidAddCapability(e => { + this._register(this.capabilities.onDidAddCapabilityType(e => { this._logService.debug('terminalInstance added capability', e); if (e === TerminalCapability.CwdDetection) { this.capabilities.get(TerminalCapability.CwdDetection)?.onDidChangeCwd(e => { @@ -429,7 +429,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { }); } })); - this._register(this.capabilities.onDidRemoveCapability(e => this._logService.debug('terminalInstance removed capability', e))); + this._register(this.capabilities.onDidRemoveCapabilityType(e => this._logService.debug('terminalInstance removed capability', e))); // Resolve just the icon ahead of time so that it shows up immediately in the tabs. This is // disabled in remote because this needs to be sync and the OS may differ on the remote @@ -804,7 +804,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._updateProcessCwd(); } }); - this._register(this.capabilities.onDidAddCapability(e => { + this._register(this.capabilities.onDidAddCapabilityType(e => { if (e === TerminalCapability.CwdDetection) { onKeyListener?.dispose(); onKeyListener = undefined; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index c9f6c946dc2..d24ff9c4c87 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -1246,7 +1246,7 @@ export class TerminalService extends Disposable implements ITerminalService { } // Added capabilities - const addCapabilityMultiplexer = this.onInstanceEvent(instance => Event.map(instance.capabilities.onDidAddCapability2, changeEvent => ({ instance, changeEvent }))); + const addCapabilityMultiplexer = this.onInstanceEvent(instance => Event.map(instance.capabilities.onDidAddCapability, changeEvent => ({ instance, changeEvent }))); addCapabilityMultiplexer.event(e => { if (e.changeEvent.id === capabilityId) { addCapability(e.instance, e.changeEvent.capability); @@ -1254,7 +1254,7 @@ export class TerminalService extends Disposable implements ITerminalService { }); // Removed capabilities - const removeCapabilityMultiplexer = this.onInstanceEvent(instance => instance.capabilities.onDidRemoveCapability2); + const removeCapabilityMultiplexer = this.onInstanceEvent(instance => instance.capabilities.onDidRemoveCapability); removeCapabilityMultiplexer.event(e => { if (e.id === capabilityId) { capabilityListeners.deleteAndDispose(e.capability); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index d874b170e90..db205d31f34 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -112,7 +112,7 @@ export class TerminalViewPane extends ViewPane { } })); this._register(this._terminalService.onDidCreateInstance((i) => { - i.capabilities.onDidAddCapability(c => { + i.capabilities.onDidAddCapabilityType(c => { if (c === TerminalCapability.CommandDetection && this._gutterDecorationsEnabled()) { this._parentDomElement?.classList.add('shell-integration'); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index b3f68aa4e6d..59898ee1b40 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -69,8 +69,8 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { })); this._register(this._themeService.onDidColorThemeChange(() => this._refreshStyles(true))); this._updateDecorationVisibility(); - this._register(this._capabilities.onDidAddCapability(c => this._createCapabilityDisposables(c))); - this._register(this._capabilities.onDidRemoveCapability(c => this._removeCapabilityDisposables(c))); + this._register(this._capabilities.onDidAddCapabilityType(c => this._createCapabilityDisposables(c))); + this._register(this._capabilities.onDidRemoveCapabilityType(c => this._removeCapabilityDisposables(c))); this._register(lifecycleService.onWillShutdown(() => this._disposeAllDecorations())); this._terminalDecorationHoverService = instantiationService.createInstance(TerminalDecorationHoverManager); } diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts index deb033af52f..6700e44426f 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts @@ -14,8 +14,8 @@ suite('TerminalCapabilityStore', () => { setup(() => { store = new TerminalCapabilityStore(); - store.onDidAddCapability(e => addEvents.push(e)); - store.onDidRemoveCapability(e => removeEvents.push(e)); + store.onDidAddCapabilityType(e => addEvents.push(e)); + store.onDidRemoveCapabilityType(e => removeEvents.push(e)); addEvents = []; removeEvents = []; }); @@ -61,8 +61,8 @@ suite('TerminalCapabilityStoreMultiplexer', () => { setup(() => { multiplexer = new TerminalCapabilityStoreMultiplexer(); - multiplexer.onDidAddCapability(e => addEvents.push(e)); - multiplexer.onDidRemoveCapability(e => removeEvents.push(e)); + multiplexer.onDidAddCapabilityType(e => addEvents.push(e)); + multiplexer.onDidRemoveCapabilityType(e => removeEvents.push(e)); store1 = new TerminalCapabilityStore(); store2 = new TerminalCapabilityStore(); addEvents = []; diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts index 9f1ccc65e8c..365dd9f00d1 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts @@ -89,7 +89,7 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, if (commandDetectionCapability) { this._registerCommandHandlers(); } else { - this._register(this._capabilities.onDidAddCapability(c => { + this._register(this._capabilities.onDidAddCapabilityType(c => { if (c === TerminalCapability.CommandDetection) { this._registerCommandHandlers(); } From 3aa165909ecff1c264c40916c4e17f9c7646cbb2 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 25 Aug 2023 09:26:21 -0700 Subject: [PATCH 219/607] fix #186404 --- src/vs/editor/browser/controller/textAreaHandler.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 94fa1a8bdb1..4f5a2db0718 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -189,7 +189,9 @@ export class TextAreaHandler extends ViewPart { this.textArea.setAttribute('aria-roledescription', nls.localize('editor', "editor")); this.textArea.setAttribute('aria-multiline', 'true'); this.textArea.setAttribute('aria-haspopup', 'false'); - this.textArea.setAttribute('aria-autocomplete', 'both'); + if (!options.get(EditorOption.readOnly)) { + this.textArea.setAttribute('aria-autocomplete', 'both'); + } this._ensureReadOnlyAttribute(); From 259a415e8890311727ac8476b9b8d857092e8a45 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 25 Aug 2023 09:27:33 -0700 Subject: [PATCH 220/607] inline --- src/vs/editor/browser/controller/textAreaHandler.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 4f5a2db0718..d1db497e636 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -188,10 +188,7 @@ export class TextAreaHandler extends ViewPart { this.textArea.setAttribute('role', 'textbox'); this.textArea.setAttribute('aria-roledescription', nls.localize('editor', "editor")); this.textArea.setAttribute('aria-multiline', 'true'); - this.textArea.setAttribute('aria-haspopup', 'false'); - if (!options.get(EditorOption.readOnly)) { - this.textArea.setAttribute('aria-autocomplete', 'both'); - } + this.textArea.setAttribute('aria-autocomplete', options.get(EditorOption.readOnly) ? 'none' : 'both'); this._ensureReadOnlyAttribute(); From aced05cf7ca2d378ff6dc0d5f1f71e8f31eceaab Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Fri, 25 Aug 2023 09:35:31 -0700 Subject: [PATCH 221/607] Remove Semantic Similarity Provider (#191039) Now that RelatedInformationProvider has landed ref https://github.com/microsoft/vscode/issues/190909 --- .../api/browser/extensionHost.contribution.ts | 1 - .../browser/mainThreadSemanticSimilarity.ts | 36 ------ .../workbench/api/common/extHost.api.impl.ts | 6 - .../workbench/api/common/extHost.protocol.ts | 11 -- .../api/common/extHostAiRelatedInformation.ts | 4 +- .../api/common/extHostSemanticSimilarity.ts | 47 -------- src/vs/workbench/browser/quickaccess.ts | 2 +- .../browser/workbench.contribution.ts | 4 +- .../preferences/browser/preferencesSearch.ts | 53 ++------- .../browser/commandsQuickAccess.ts | 57 ++------- .../common/extensionsApiProposals.ts | 1 - .../common/semanticSimilarityService.ts | 108 ------------------ .../vscode.proposed.semanticSimilarity.d.ts | 22 ---- 13 files changed, 25 insertions(+), 327 deletions(-) delete mode 100644 src/vs/workbench/api/browser/mainThreadSemanticSimilarity.ts delete mode 100644 src/vs/workbench/api/common/extHostSemanticSimilarity.ts delete mode 100644 src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts delete mode 100644 src/vscode-dts/vscode.proposed.semanticSimilarity.d.ts diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 8a71fe97ce1..9217613b50a 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -87,7 +87,6 @@ import './mainThreadTesting'; import './mainThreadSecretState'; import './mainThreadShare'; import './mainThreadProfilContentHandlers'; -import './mainThreadSemanticSimilarity'; import './mainThreadAiRelatedInformation'; import './mainThreadAiEmbeddingVector'; import './mainThreadIssueReporter'; diff --git a/src/vs/workbench/api/browser/mainThreadSemanticSimilarity.ts b/src/vs/workbench/api/browser/mainThreadSemanticSimilarity.ts deleted file mode 100644 index 07f11ad954e..00000000000 --- a/src/vs/workbench/api/browser/mainThreadSemanticSimilarity.ts +++ /dev/null @@ -1,36 +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 { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; -import { ExtHostContext, ExtHostSemanticSimilarityShape, MainContext, MainThreadSemanticSimilarityShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { ISemanticSimilarityProvider, ISemanticSimilarityService } from 'vs/workbench/services/semanticSimilarity/common/semanticSimilarityService'; - -@extHostNamedCustomer(MainContext.MainThreadSemanticSimilarity) -export class MainThreadSemanticSimilarity extends Disposable implements MainThreadSemanticSimilarityShape { - private readonly _proxy: ExtHostSemanticSimilarityShape; - private readonly _registrations = this._register(new DisposableMap()); - - constructor( - context: IExtHostContext, - @ISemanticSimilarityService private readonly _semanticSimilarityService: ISemanticSimilarityService - ) { - super(); - this._proxy = context.getProxy(ExtHostContext.ExtHostSemanticSimilarity); - } - - $registerSemanticSimilarityProvider(handle: number): void { - const provider: ISemanticSimilarityProvider = { - provideSimilarityScore: (string1, comparisons, token) => { - return this._proxy.$provideSimilarityScore(handle, string1, comparisons, token); - }, - }; - this._registrations.set(handle, this._semanticSimilarityService.registerSemanticSimilarityProvider(provider)); - } - - $unregisterSemanticSimilarityProvider(handle: number): void { - this._registrations.deleteAndDispose(handle); - } -} diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index acdb35b1b1e..39008b38a24 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -100,7 +100,6 @@ import { ExtHostQuickDiff } from 'vs/workbench/api/common/extHostQuickDiff'; import { ExtHostChat } from 'vs/workbench/api/common/extHostChat'; import { ExtHostInteractiveEditor } from 'vs/workbench/api/common/extHostInlineChat'; import { ExtHostNotebookDocumentSaveParticipant } from 'vs/workbench/api/common/extHostNotebookDocumentSaveParticipant'; -import { ExtHostSemanticSimilarity } from 'vs/workbench/api/common/extHostSemanticSimilarity'; import { ExtHostIssueReporter } from 'vs/workbench/api/common/extHostIssueReporter'; import { IExtHostManagedSockets } from 'vs/workbench/api/common/extHostManagedSockets'; import { ExtHostShare } from 'vs/workbench/api/common/extHostShare'; @@ -212,7 +211,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostChatSlashCommands = rpcProtocol.set(ExtHostContext.ExtHostChatSlashCommands, new ExtHostChatSlashCommands(rpcProtocol, extHostChatProvider, extHostLogService)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostChat = rpcProtocol.set(ExtHostContext.ExtHostChat, new ExtHostChat(rpcProtocol, extHostLogService)); - const extHostSemanticSimilarity = rpcProtocol.set(ExtHostContext.ExtHostSemanticSimilarity, new ExtHostSemanticSimilarity(rpcProtocol)); const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); const extHostAiEmbeddingVector = rpcProtocol.set(ExtHostContext.ExtHostAiEmbeddingVector, new ExtHostAiEmbeddingVector(rpcProtocol)); const extHostIssueReporter = rpcProtocol.set(ExtHostContext.ExtHostIssueReporter, new ExtHostIssueReporter(rpcProtocol)); @@ -1324,10 +1322,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I // namespace: ai const ai: typeof vscode.ai = { - registerSemanticSimilarityProvider(provider: vscode.SemanticSimilarityProvider) { - checkProposedApiEnabled(extension, 'semanticSimilarity'); - return extHostSemanticSimilarity.registerSemanticSimilarityProvider(extension, provider); - }, getRelatedInformation(query: string, types: vscode.RelatedInformationType[]): Thenable { checkProposedApiEnabled(extension, 'aiRelatedInformation'); return extHostAiRelatedInformation.getRelatedInformation(extension, query, types); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 82646f45cac..ba0c71098b7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1665,15 +1665,6 @@ export interface ExtHostAuthenticationShape { $setProviders(providers: AuthenticationProviderInformation[]): Promise; } -export interface ExtHostSemanticSimilarityShape { - $provideSimilarityScore(handle: number, string1: string, comparisons: string[], token: CancellationToken): Promise; -} - -export interface MainThreadSemanticSimilarityShape extends IDisposable { - $registerSemanticSimilarityProvider(handle: number): void; - $unregisterSemanticSimilarityProvider(handle: number): void; -} - export interface ExtHostAiRelatedInformationShape { $provideAiRelatedInformation(handle: number, query: string, token: CancellationToken): Promise; } @@ -2672,7 +2663,6 @@ export const MainContext = { MainThreadTimeline: createProxyIdentifier('MainThreadTimeline'), MainThreadTesting: createProxyIdentifier('MainThreadTesting'), MainThreadLocalization: createProxyIdentifier('MainThreadLocalizationShape'), - MainThreadSemanticSimilarity: createProxyIdentifier('MainThreadSemanticSimilarity'), MainThreadAiRelatedInformation: createProxyIdentifier('MainThreadAiRelatedInformation'), MainThreadAiEmbeddingVector: createProxyIdentifier('MainThreadAiEmbeddingVector'), MainThreadIssueReporter: createProxyIdentifier('MainThreadIssueReporter'), @@ -2734,7 +2724,6 @@ export const ExtHostContext = { ExtHostChatSlashCommands: createProxyIdentifier('ExtHostChatSlashCommands'), ExtHostChatVariables: createProxyIdentifier('ExtHostChatVariables'), ExtHostChatProvider: createProxyIdentifier('ExtHostChatProvider'), - ExtHostSemanticSimilarity: createProxyIdentifier('ExtHostSemanticSimilarity'), ExtHostAiRelatedInformation: createProxyIdentifier('ExtHostAiRelatedInformation'), ExtHostAiEmbeddingVector: createProxyIdentifier('ExtHostAiEmbeddingVector'), ExtHostTheming: createProxyIdentifier('ExtHostTheming'), diff --git a/src/vs/workbench/api/common/extHostAiRelatedInformation.ts b/src/vs/workbench/api/common/extHostAiRelatedInformation.ts index 4cb934e1b1e..5594a8adf4c 100644 --- a/src/vs/workbench/api/common/extHostAiRelatedInformation.ts +++ b/src/vs/workbench/api/common/extHostAiRelatedInformation.ts @@ -20,12 +20,12 @@ export class ExtHostRelatedInformation implements ExtHostAiRelatedInformationSha async $provideAiRelatedInformation(handle: number, query: string, token: CancellationToken): Promise { if (this._relatedInformationProviders.size === 0) { - throw new Error('No semantic similarity providers registered'); + throw new Error('No related information providers registered'); } const provider = this._relatedInformationProviders.get(handle); if (!provider) { - throw new Error('Semantic similarity provider not found'); + throw new Error('related information provider not found'); } const result = await provider.provideRelatedInformation(query, token) ?? []; diff --git a/src/vs/workbench/api/common/extHostSemanticSimilarity.ts b/src/vs/workbench/api/common/extHostSemanticSimilarity.ts deleted file mode 100644 index 430ee4c9367..00000000000 --- a/src/vs/workbench/api/common/extHostSemanticSimilarity.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 { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { ExtHostSemanticSimilarityShape, IMainContext, MainContext, MainThreadSemanticSimilarityShape } from 'vs/workbench/api/common/extHost.protocol'; -import type { CancellationToken, SemanticSimilarityProvider } from 'vscode'; -import { Disposable } from 'vs/workbench/api/common/extHostTypes'; - -export class ExtHostSemanticSimilarity implements ExtHostSemanticSimilarityShape { - private _semanticSimilarityProviders: Map = new Map(); - private _nextHandle = 0; - - private readonly _proxy: MainThreadSemanticSimilarityShape; - - constructor( - mainContext: IMainContext - ) { - this._proxy = mainContext.getProxy(MainContext.MainThreadSemanticSimilarity); - } - - async $provideSimilarityScore(handle: number, string1: string, comparisons: string[], token: CancellationToken): Promise { - if (this._semanticSimilarityProviders.size === 0) { - throw new Error('No semantic similarity providers registered'); - } - - const provider = this._semanticSimilarityProviders.get(handle); - if (!provider) { - throw new Error('Semantic similarity provider not found'); - } - - const result = await provider.provideSimilarityScore(string1, comparisons, token); - return result; - } - - registerSemanticSimilarityProvider(extension: IExtensionDescription, provider: SemanticSimilarityProvider): Disposable { - const handle = this._nextHandle; - this._nextHandle++; - this._semanticSimilarityProviders.set(handle, provider); - this._proxy.$registerSemanticSimilarityProvider(handle); - return new Disposable(() => { - this._proxy.$unregisterSemanticSimilarityProvider(handle); - this._semanticSimilarityProviders.delete(handle); - }); - } -} diff --git a/src/vs/workbench/browser/quickaccess.ts b/src/vs/workbench/browser/quickaccess.ts index 7aaec862f99..7c4ff4fa763 100644 --- a/src/vs/workbench/browser/quickaccess.ts +++ b/src/vs/workbench/browser/quickaccess.ts @@ -23,7 +23,7 @@ export interface IWorkbenchQuickAccessConfiguration { readonly preserveInput: boolean; readonly experimental: { readonly suggestCommands: boolean; - readonly useSemanticSimilarity: boolean; + readonly enableNaturalLanguageSearch: boolean; }; }; readonly quickOpen: { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index da60053abeb..5b1d769a4a4 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -381,10 +381,10 @@ const registry = Registry.as(ConfigurationExtensions.Con 'description': localize('suggestCommands', "Controls whether the command palette should have a list of commonly used commands."), 'default': false }, - 'workbench.commandPalette.experimental.useSemanticSimilarity': { + 'workbench.commandPalette.experimental.enableNaturalLanguageSearch': { 'type': 'boolean', tags: ['experimental'], - 'description': localize('useSemanticSimilarity', "Controls whether the command palette should include similar commands. You must have an extension installed that provides Semantic Similarity."), + 'description': localize('enableNaturalLanguageSearch', "Controls whether the command palette should include similar commands. You must have an extension installed that provides Natural Language support."), 'default': true }, 'workbench.quickOpen.closeOnFocusLost': { diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index fe7ce4b6848..d0a927c373c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -20,7 +20,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ISemanticSimilarityService } from 'vs/workbench/services/semanticSimilarity/common/semanticSimilarityService'; import { IAiRelatedInformationService, RelatedInformationType, SettingInformationResult } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; export interface IEndpointDetails { @@ -304,8 +303,7 @@ class RemoteSearchKeysProvider { private currentPreferencesModel: ISettingsEditorModel | undefined; constructor( - private readonly aiRelatedInformationService: IAiRelatedInformationService, - private readonly semanticSimilarityService: ISemanticSimilarityService + private readonly aiRelatedInformationService: IAiRelatedInformationService ) { } updateModel(preferencesModel: ISettingsEditorModel) { @@ -323,7 +321,7 @@ class RemoteSearchKeysProvider { if ( !this.currentPreferencesModel || - (!this.semanticSimilarityService.isEnabled() && !this.aiRelatedInformationService.isEnabled()) + !this.aiRelatedInformationService.isEnabled() ) { return; } @@ -351,17 +349,16 @@ class RemoteSearchKeysProvider { } export class RemoteSearchProvider implements ISearchProvider { - private static readonly SEMANTIC_SIMILARITY_THRESHOLD = 0.73; - private static readonly SEMANTIC_SIMILARITY_MAX_PICKS = 15; + private static readonly AI_RELATED_INFORMATION_THRESHOLD = 0.73; + private static readonly AI_RELATED_INFORMATION_MAX_PICKS = 15; private readonly _keysProvider: RemoteSearchKeysProvider; private _filter: string = ''; constructor( - @ISemanticSimilarityService private readonly semanticSimilarityService: ISemanticSimilarityService, @IAiRelatedInformationService private readonly aiRelatedInformationService: IAiRelatedInformationService ) { - this._keysProvider = new RemoteSearchKeysProvider(aiRelatedInformationService, semanticSimilarityService); + this._keysProvider = new RemoteSearchKeysProvider(aiRelatedInformationService); } setFilter(filter: string) { @@ -371,50 +368,18 @@ export class RemoteSearchProvider implements ISearchProvider { async searchModel(preferencesModel: ISettingsEditorModel, token?: CancellationToken | undefined): Promise { if ( !this._filter || - (!this.semanticSimilarityService.isEnabled() && !this.aiRelatedInformationService.isEnabled())) { + !this.aiRelatedInformationService.isEnabled() + ) { return null; } this._keysProvider.updateModel(preferencesModel); - const filterMatches = this.aiRelatedInformationService.isEnabled() - ? await this.getAiRelatedInformationItems(token) - : this.semanticSimilarityService.isEnabled() - ? await this.getSemanticSimilarityItems(token) - : []; return { - filterMatches + filterMatches: await this.getAiRelatedInformationItems(token) }; } - // TODO: Remove this when all semantic similarity providers are migrated to aiRelatedInformationService - private async getSemanticSimilarityItems(token?: CancellationToken | undefined) { - const settingKeys = this._keysProvider.getSettingKeys(); - const settingsRecord = this._keysProvider.getSettingsRecord(); - - const scores = await this.semanticSimilarityService.getSimilarityScore(this._filter, settingKeys, token ?? CancellationToken.None); - const filterMatches: ISettingMatch[] = []; - const sortedIndices = scores.map((_, i) => i).sort((a, b) => scores[b] - scores[a]); - let numOfSmartPicks = 0; - for (const i of sortedIndices) { - const score = scores[i]; - if (score < RemoteSearchProvider.SEMANTIC_SIMILARITY_THRESHOLD || numOfSmartPicks === RemoteSearchProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { - break; - } - - const pick = settingKeys[i]; - filterMatches.push({ - setting: settingsRecord[pick], - matches: [settingsRecord[pick].range], - matchType: SettingMatchType.RemoteMatch, - score - }); - numOfSmartPicks++; - } - - return filterMatches; - } - private async getAiRelatedInformationItems(token?: CancellationToken | undefined) { const settingsRecord = this._keysProvider.getSettingsRecord(); @@ -423,7 +388,7 @@ export class RemoteSearchProvider implements ISearchProvider { relatedInformation.sort((a, b) => b.weight - a.weight); for (const info of relatedInformation) { - if (info.weight < RemoteSearchProvider.SEMANTIC_SIMILARITY_THRESHOLD || filterMatches.length === RemoteSearchProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { + if (info.weight < RemoteSearchProvider.AI_RELATED_INFORMATION_THRESHOLD || filterMatches.length === RemoteSearchProvider.AI_RELATED_INFORMATION_MAX_PICKS) { break; } const pick = info.setting; diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index 4df3b8e287f..beff3204133 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -33,16 +33,15 @@ import { IPreferencesService } from 'vs/workbench/services/preferences/common/pr import { stripIcons } from 'vs/base/common/iconLabels'; import { isFirefox } from 'vs/base/browser/browser'; import { IProductService } from 'vs/platform/product/common/productService'; -import { ISemanticSimilarityService } from 'vs/workbench/services/semanticSimilarity/common/semanticSimilarityService'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { ASK_QUICK_QUESTION_ACTION_ID } from 'vs/workbench/contrib/chat/browser/actions/chatQuickInputActions'; import { CommandInformationResult, IAiRelatedInformationService, RelatedInformationType } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAccessProvider { - private static SEMANTIC_SIMILARITY_MAX_PICKS = 3; - private static SEMANTIC_SIMILARITY_THRESHOLD = 0.8; - private static SEMANTIC_SIMILARITY_DEBOUNCE = 200; + private static AI_RELATED_INFORMATION_MAX_PICKS = 3; + private static AI_RELATED_INFORMATION_THRESHOLD = 0.8; + private static AI_RELATED_INFORMATION_DEBOUNCE = 200; // If extensions are not yet registered, we wait for a little moment to give them // a chance to register so that the complete set of commands shows up as result @@ -50,7 +49,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce // functional. private readonly extensionRegistrationRace = raceTimeout(this.extensionService.whenInstalledExtensionsRegistered(), 800); - private useSemanticSimilarity = false; + private useAiRelatedInfo = false; protected get activeTextEditorControl(): IEditor | undefined { return this.editorService.activeTextEditorControl; } @@ -75,7 +74,6 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IProductService private readonly productService: IProductService, - @ISemanticSimilarityService private readonly semanticSimilarityService: ISemanticSimilarityService, @IAiRelatedInformationService private readonly aiRelatedInformationService: IAiRelatedInformationService, @IChatService private readonly chatService: IChatService ) { @@ -110,7 +108,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce ? new Set(this.productService.commandPaletteSuggestedCommandIds) : undefined; this.options.suggestedCommandIds = suggestedCommandIds; - this.useSemanticSimilarity = config.experimental.useSemanticSimilarity; + this.useAiRelatedInfo = config.experimental.enableNaturalLanguageSearch; } protected async getCommandPicks(token: CancellationToken): Promise> { @@ -140,10 +138,10 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce protected hasAdditionalCommandPicks(filter: string, token: CancellationToken): boolean { if ( - !this.useSemanticSimilarity + !this.useAiRelatedInfo || token.isCancellationRequested || filter === '' - || !(this.semanticSimilarityService.isEnabled() || this.aiRelatedInformationService.isEnabled()) + || !this.aiRelatedInformationService.isEnabled() ) { return false; } @@ -160,12 +158,8 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce try { // Wait a bit to see if the user is still typing - await timeout(CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_DEBOUNCE, token); - additionalPicks = this.aiRelatedInformationService.isEnabled() - ? await this.getRelatedInformationPicks(allPicks, picksSoFar, filter, token) - : this.semanticSimilarityService.isEnabled() - ? await this.getSemanticSimilarityPicks(allPicks, picksSoFar, filter, token) - : []; + await timeout(CommandsQuickAccessProvider.AI_RELATED_INFORMATION_DEBOUNCE, token); + additionalPicks = await this.getRelatedInformationPicks(allPicks, picksSoFar, filter, token); } catch (e) { return []; } @@ -173,7 +167,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce if (additionalPicks.length) { additionalPicks.unshift({ type: 'separator', - label: localize('semanticSimilarity', "similar commands") + label: localize('similarCommands', "similar commands") }); } @@ -195,35 +189,6 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce return additionalPicks; } - private async getSemanticSimilarityPicks(allPicks: ICommandQuickPick[], picksSoFar: ICommandQuickPick[], filter: string, token: CancellationToken) { - const format = allPicks.map(p => p.commandId); - const scores = await this.semanticSimilarityService.getSimilarityScore(filter, format, token); - - if (token.isCancellationRequested) { - return []; - } - - const sortedIndices = scores.map((_, i) => i).sort((a, b) => scores[b] - scores[a]); - const setOfPicksSoFar = new Set(picksSoFar.map(p => p.commandId)); - const additionalPicks = new Array(); - - let numOfSmartPicks = 0; - for (const i of sortedIndices) { - const score = scores[i]; - if (score < CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_THRESHOLD || numOfSmartPicks === CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { - break; - } - - const pick = allPicks[i]; - if (!setOfPicksSoFar.has(pick.commandId)) { - additionalPicks.push(pick); - numOfSmartPicks++; - } - } - - return additionalPicks; - } - private async getRelatedInformationPicks(allPicks: ICommandQuickPick[], picksSoFar: ICommandQuickPick[], filter: string, token: CancellationToken) { const relatedInformation = await this.aiRelatedInformationService.getRelatedInformation( filter, @@ -238,7 +203,7 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce const additionalPicks = new Array(); for (const info of relatedInformation) { - if (info.weight < CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_THRESHOLD || additionalPicks.length === CommandsQuickAccessProvider.SEMANTIC_SIMILARITY_MAX_PICKS) { + if (info.weight < CommandsQuickAccessProvider.AI_RELATED_INFORMATION_THRESHOLD || additionalPicks.length === CommandsQuickAccessProvider.AI_RELATED_INFORMATION_MAX_PICKS) { break; } const pick = allPicks.find(p => p.commandId === info.command && !setOfPicksSoFar.has(p.commandId)); diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 835775b8fbe..ee2364c02c5 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -79,7 +79,6 @@ export const allApiProposals = Object.freeze({ scmSelectedProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts', scmTextDocument: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmTextDocument.d.ts', scmValidation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmValidation.d.ts', - semanticSimilarity: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.semanticSimilarity.d.ts', shareProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.shareProvider.d.ts', showLocal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.showLocal.d.ts', tabInputTextMerge: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts', diff --git a/src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts b/src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts deleted file mode 100644 index e322503d117..00000000000 --- a/src/vs/workbench/services/semanticSimilarity/common/semanticSimilarityService.ts +++ /dev/null @@ -1,108 +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 { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { CancelablePromise, createCancelablePromise, raceCancellablePromises, timeout } from 'vs/base/common/async'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { StopWatch } from 'vs/base/common/stopwatch'; -import { ILogService } from 'vs/platform/log/common/log'; - -/** - * @deprecated Use `IAiRelatedInformationService` instead. - */ -export const ISemanticSimilarityService = createDecorator('ISemanticSimilarityService'); - -/** - * @deprecated Use `IAiRelatedInformationService` instead. - */ -export interface ISemanticSimilarityService { - readonly _serviceBrand: undefined; - - isEnabled(): boolean; - getSimilarityScore(string1: string, comparisons: string[], token: CancellationToken): Promise; - registerSemanticSimilarityProvider(provider: ISemanticSimilarityProvider): IDisposable; -} - -export interface ISemanticSimilarityProvider { - provideSimilarityScore(string1: string, comparisons: string[], token: CancellationToken): Promise; -} - -export class SemanticSimilarityService implements ISemanticSimilarityService { - readonly _serviceBrand: undefined; - - static readonly DEFAULT_TIMEOUT = 1000 * 10; // 10 seconds - - private readonly _providers: ISemanticSimilarityProvider[] = []; - - constructor(@ILogService private readonly logService: ILogService) { } - - isEnabled(): boolean { - return this._providers.length > 0; - } - - registerSemanticSimilarityProvider(provider: ISemanticSimilarityProvider): IDisposable { - this._providers.push(provider); - return { - dispose: () => { - const index = this._providers.indexOf(provider); - if (index >= 0) { - this._providers.splice(index, 1); - } - } - }; - } - - async getSimilarityScore(string1: string, comparisons: string[], token: CancellationToken): Promise { - if (this._providers.length === 0) { - throw new Error('No semantic similarity providers registered'); - } - - const stopwatch = StopWatch.create(); - - const cancellablePromises: Array> = []; - - const timer = timeout(SemanticSimilarityService.DEFAULT_TIMEOUT); - const disposable = token.onCancellationRequested(() => { - disposable.dispose(); - timer.cancel(); - }); - - for (const provider of this._providers) { - cancellablePromises.push(createCancelablePromise(async t => { - try { - return await provider.provideSimilarityScore(string1, comparisons, t); - } catch (e) { - // logged in extension host - } - // Wait for the timer to finish to allow for another provider to resolve. - // Alternatively, if something resolved, or we've timed out, this will throw - // as expected. - await timer; - throw new Error('Semantic similarity provider timed out'); - })); - } - - cancellablePromises.push(createCancelablePromise(async (t) => { - const disposable = t.onCancellationRequested(() => { - timer.cancel(); - disposable.dispose(); - }); - await timer; - throw new Error('Semantic similarity provider timed out'); - })); - - try { - const result = await raceCancellablePromises(cancellablePromises); - return result; - } finally { - stopwatch.stop(); - this.logService.trace(`[SemanticSimilarityService]: getSimilarityScore took ${stopwatch.elapsed()}ms`); - } - } -} - -registerSingleton(ISemanticSimilarityService, SemanticSimilarityService, InstantiationType.Delayed); diff --git a/src/vscode-dts/vscode.proposed.semanticSimilarity.d.ts b/src/vscode-dts/vscode.proposed.semanticSimilarity.d.ts deleted file mode 100644 index d2ffb823386..00000000000 --- a/src/vscode-dts/vscode.proposed.semanticSimilarity.d.ts +++ /dev/null @@ -1,22 +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' { - export interface SemanticSimilarityProvider { - /** - * Computes the semantic similarity score between two strings. - * @param string1 The string to compare to all other strings. - * @param comparisons An array of strings to compare string1 to. An array allows you to batch multiple comparisons in one call. - * @param token A cancellation token. - * @return A promise that resolves to the semantic similarity scores between string1 and each string in comparisons. - * The score should be a number between 0 and 1, where 0 means no similarity and 1 means - * perfect similarity. - */ - provideSimilarityScore(string1: string, comparisons: string[], token: CancellationToken): Thenable; - } - export namespace ai { - export function registerSemanticSimilarityProvider(provider: SemanticSimilarityProvider): Disposable; - } -} From 54031a88cc8974fbbdf537b1143026f59c2e801d Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 25 Aug 2023 09:37:22 -0700 Subject: [PATCH 222/607] fix #189234 --- .../accessibility/browser/accessibleView.ts | 2 +- .../browser/terminalAccessibilityHelp.ts | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 02c1f17b6f6..ed6152bfbb5 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -335,7 +335,7 @@ class AccessibleView extends Disposable { } } - this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', '\n\nExit this dialog via the Escape key.'); + this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', '\nExit this dialog via the Escape key.'); this._updateContextKeys(provider, true); this._getTextModel(URI.from({ path: `accessible-view-${provider.verbositySettingKey}`, scheme: 'accessible-view', fragment: this._currentContent })).then((model) => { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 2504d461fb2..daa116faa5b 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -6,7 +6,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { format } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -47,7 +46,6 @@ export class TerminalAccessibleContentProvider extends Disposable implements IAc _xterm: Pick & { raw: Terminal }, @IInstantiationService _instantiationService: IInstantiationService, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IAccessibilityService private readonly _accessibilityService: IAccessibilityService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @ICommandService private readonly _commandService: ICommandService ) { @@ -56,16 +54,8 @@ export class TerminalAccessibleContentProvider extends Disposable implements IAc } private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { - const kb = this._keybindingService.lookupKeybindings(commandId); - switch (kb.length) { - case 0: - return format(noKbMsg, commandId); - case 1: - return format(msg, kb[0].getAriaLabel()); - } - // Run recent command has multiple keybindings. lookupKeybinding just returns the first one regardless of the when context. - // Thus, we have to check if accessibility mode is enabled to determine which keybinding to use. - return this._accessibilityService.isScreenReaderOptimized() ? format(msg, kb[1].getAriaLabel()) : format(msg, kb[0].getAriaLabel()); + const kb = this._keybindingService.lookupKeybinding(commandId, this._contextKeyService)?.getAriaLabel(); + return !kb ? format(noKbMsg, commandId) : format(msg, kb); } provideContent(): string { From 6655ff4fa8ea0fef9d1a801475aedf3610582281 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 25 Aug 2023 09:39:29 -0700 Subject: [PATCH 223/607] re-add part --- .../accessibility/browser/terminalAccessibilityHelp.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index daa116faa5b..206e04d47cc 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -6,6 +6,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { format } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -47,13 +48,20 @@ export class TerminalAccessibleContentProvider extends Disposable implements IAc @IInstantiationService _instantiationService: IInstantiationService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @ICommandService private readonly _commandService: ICommandService + @ICommandService private readonly _commandService: ICommandService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { super(); this._hasShellIntegration = _xterm.shellIntegration.status === ShellIntegrationStatus.VSCode; } private _descriptionForCommand(commandId: string, msg: string, noKbMsg: string): string { + if (commandId === TerminalCommandId.RunRecentCommand) { + const kb = this._keybindingService.lookupKeybindings(commandId); + // Run recent command has multiple keybindings. lookupKeybinding just returns the first one regardless of the when context. + // Thus, we have to check if accessibility mode is enabled to determine which keybinding to use. + return this._accessibilityService.isScreenReaderOptimized() ? format(msg, kb[1].getAriaLabel()) : format(msg, kb[0].getAriaLabel()); + } const kb = this._keybindingService.lookupKeybinding(commandId, this._contextKeyService)?.getAriaLabel(); return !kb ? format(noKbMsg, commandId) : format(msg, kb); } From 5b5fbe166d136a2a24d17de82acc14f20da9760f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:49:39 -0700 Subject: [PATCH 224/607] Simplify api --- .../api/browser/mainThreadTerminalService.ts | 10 ++---- .../workbench/api/common/extHost.api.impl.ts | 5 --- .../workbench/api/common/extHost.protocol.ts | 9 ++--- .../api/common/extHostTerminalService.ts | 29 +++------------- ....proposed.terminalExecuteCommandEvent.d.ts | 34 ++++--------------- 5 files changed, 16 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index b79eb926d98..3df20a04d66 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -242,10 +242,8 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape commandLine: e.data.command, // TODO: Convert to URI if possible cwd: e.data.cwd, - result: { - exitCode: e.data.exitCode, - output: e.data.getOutput() ?? '' - } + exitCode: e.data.exitCode, + output: e.data.getOutput() }); }); this._sendCommandEventListener.value = multiplexer; @@ -334,10 +332,6 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape this._proxy.$acceptTerminalProcessData(terminalId, data); } - // private _onWillExecuteCommand(terminalId: number, command: ITerminalCommandDto): void { - // this._proxy.$acceptWillExecuteCommand(terminalId, command); - // } - private _onDidExecuteCommand(terminalId: number, command: ITerminalCommandDto): void { this._proxy.$acceptDidExecuteCommand(terminalId, command); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index e04d6de10a3..f0720ab47a3 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -698,11 +698,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I checkProposedApiEnabled(extension, 'terminalDataWriteEvent'); return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables); }, - onWillExecuteTerminalCommand(listener, thisArg?, disposables?) { - checkProposedApiEnabled(extension, 'terminalExecuteCommandEvent'); - return extHostTerminalService.onWillExecuteTerminalCommand(listener, thisArg, disposables); - - }, onDidExecuteTerminalCommand(listener, thisArg?, disposables?) { checkProposedApiEnabled(extension, 'terminalExecuteCommandEvent'); return extHostTerminalService.onDidExecuteTerminalCommand(listener, thisArg, disposables); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d1bc7c19b5d..344df01f4e8 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -2092,12 +2092,10 @@ export interface TerminalCommandMatchResultDto { } export interface ITerminalCommandDto { - commandLine: string; + commandLine: string | undefined; cwd: URI | string | undefined; - result: { - exitCode: number | undefined; - output: string; - }; + exitCode: number | undefined; + output: string | undefined; } export interface ExtHostTerminalServiceShape { @@ -2106,7 +2104,6 @@ export interface ExtHostTerminalServiceShape { $acceptActiveTerminalChanged(id: number | null): void; $acceptTerminalProcessId(id: number, processId: number): void; $acceptTerminalProcessData(id: number, data: string): void; - $acceptWillExecuteCommand(id: number, command: ITerminalCommandDto): void; $acceptDidExecuteCommand(id: number, command: ITerminalCommandDto): void; $acceptTerminalTitleChange(id: number, name: string): void; $acceptTerminalDimensions(id: number, cols: number, rows: number): void; diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 12f822bc5bb..51316b974cc 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -5,7 +5,7 @@ import type * as vscode from 'vscode'; import { Event, Emitter } from 'vs/base/common/event'; -import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, ITerminalDimensionsDto, ITerminalLinkDto, ExtHostTerminalIdentifier, ICommandDto, ITerminalQuickFixOpenerDto, ITerminalQuickFixExecuteTerminalCommandDto, TerminalCommandMatchResultDto } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, ITerminalDimensionsDto, ITerminalLinkDto, ExtHostTerminalIdentifier, ICommandDto, ITerminalQuickFixOpenerDto, ITerminalQuickFixExecuteTerminalCommandDto, TerminalCommandMatchResultDto, ITerminalCommandDto } from 'vs/workbench/api/common/extHost.protocol'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; @@ -40,8 +40,7 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape, ID readonly onDidChangeTerminalDimensions: Event; readonly onDidChangeTerminalState: Event; readonly onDidWriteTerminalData: Event; - readonly onWillExecuteTerminalCommand: Event; - readonly onDidExecuteTerminalCommand: Event; + readonly onDidExecuteTerminalCommand: Event; readonly onDidChangeShell: Event; createTerminal(name?: string, shellPath?: string, shellArgs?: readonly string[] | string): vscode.Terminal; @@ -403,12 +402,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I onDidRemoveLastListener: () => this._proxy.$stopSendingDataEvents() }); readonly onDidWriteTerminalData = this._onDidWriteTerminalData.event; - protected readonly _onWillExecuteCommand = new Emitter({ - onWillAddFirstListener: () => this._proxy.$startSendingCommandEvents(), - onDidRemoveLastListener: () => this._proxy.$stopSendingCommandEvents() - }); - readonly onWillExecuteTerminalCommand = this._onWillExecuteCommand.event; - protected readonly _onDidExecuteCommand = new Emitter({ + protected readonly _onDidExecuteCommand = new Emitter({ onWillAddFirstListener: () => this._proxy.$startSendingCommandEvents(), onDidRemoveLastListener: () => this._proxy.$stopSendingCommandEvents() }); @@ -521,23 +515,10 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I } } - public async $acceptWillExecuteCommand(id: number, command: any): Promise { + public async $acceptDidExecuteCommand(id: number, command: ITerminalCommandDto): Promise { const terminal = this._getTerminalById(id); if (terminal) { - this._onWillExecuteCommand.fire({ - terminal: terminal.value, - command: command - }); - } - } - - public async $acceptDidExecuteCommand(id: number, command: any): Promise { - const terminal = this._getTerminalById(id); - if (terminal) { - this._onDidExecuteCommand.fire({ - terminal: terminal.value, - command: command - }); + this._onDidExecuteCommand.fire({ terminal: terminal.value, ...command }); } } diff --git a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts index 09e8146a10d..4b3aa64400e 100644 --- a/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts +++ b/src/vscode-dts/vscode.proposed.terminalExecuteCommandEvent.d.ts @@ -7,17 +7,7 @@ declare module 'vscode' { // https://github.com/microsoft/vscode/issues/145234 - export interface TerminalWillExecuteCommandEvent { - terminal: Terminal; - command: TerminalCommand>; - } - - export interface TerminalExecuteCommandEvent { - terminal: Terminal; - command: TerminalCommand; - } - - export interface TerminalCommand> { + export interface TerminalExecutedCommand { /** * The {@link Terminal} the command was executed in. */ @@ -25,44 +15,32 @@ declare module 'vscode' { /** * The full command line that was executed, including both the command and the arguments. */ - commandLine: string; + commandLine: string | undefined; /** * The current working directory that was reported by the shell. This will be a {@link Uri} * if the string reported by the shell can reliably be mapped to the connected machine. */ cwd: Uri | string | undefined; - /** - * The result of the command. - */ - result: T; - } - - export interface TerminalExecuteCommandResult { /** * The exit code reported by the shell. */ - exitCode: number; + exitCode: number | undefined; /** * The output of the command when it has finished executing. This is the plain text shown in * the terminal buffer and does not include raw escape sequences. Depending on the shell * setup, this may include the command line as part of the output. */ - output: string; + output: string | undefined; } export namespace window { - /** - * An event that is emitted when a terminal with shell integration activated has started - * executing a command. - */ - export const onWillExecuteTerminalCommand: Event; /** * An event that is emitted when a terminal with shell integration activated has completed * executing a command. * * Note that this event will not fire if the executed command exits the shell, listen to - * {@link onDidOpenTerminal} to handle that case. + * {@link onDidCloseTerminal} to handle that case. */ - export const onDidExecuteTerminalCommand: Event; + export const onDidExecuteTerminalCommand: Event; } } From d360619c0c0796d9f19caa2f80ba8ad716a9bdba Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:52:10 -0700 Subject: [PATCH 225/607] Document EventMultiplexer --- src/vs/base/common/event.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 872263f2e9e..ae7f8701dca 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -1343,6 +1343,29 @@ export class MicrotaskEmitter extends Emitter { } } +/** + * An event emitter that multiplexes many events into a single event. + * + * @example Listen to the `onData` event of all `Thing`s, dynamically adding and removing `Thing`s + * to the multiplexer as needed. + * + * ```typescript + * const anythingDataMultiplexer = new EventMultiplexer<{ data: string }>(); + * + * const thingListeners = DisposableMap(); + * + * thingService.onDidAddThing(thing => { + * thingListeners.set(thing, anythingDataMultiplexer.add(thing.onData); + * }); + * thingService.onDidRemoveThing(thing => { + * thingListeners.deleteAndDispose(thing); + * }); + * + * anythingDataMultiplexer.event(e => { + * console.log('Something fired data ' + e.data) + * }); + * ``` + */ export class EventMultiplexer implements IDisposable { private readonly emitter: Emitter; From e3e131c96341823b5b77fb701a18d7025da66713 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 09:54:08 -0700 Subject: [PATCH 226/607] Use new event names internally --- .../capabilities/terminalCapabilityStore.ts | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts index 2589c2c7f5c..e630b8d898c 100644 --- a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts +++ b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts @@ -10,15 +10,15 @@ import { ITerminalCapabilityImplMap, ITerminalCapabilityStore, TerminalCapabilit export class TerminalCapabilityStore extends Disposable implements ITerminalCapabilityStore { private _map: Map = new Map(); - private readonly _onDidRemoveCapability = this._register(new Emitter()); - readonly onDidRemoveCapabilityType = this._onDidRemoveCapability.event; - private readonly _onDidAddCapability = this._register(new Emitter()); - readonly onDidAddCapabilityType = this._onDidAddCapability.event; + private readonly _onDidRemoveCapabilityType = this._register(new Emitter()); + readonly onDidRemoveCapabilityType = this._onDidRemoveCapabilityType.event; + private readonly _onDidAddCapabilityType = this._register(new Emitter()); + readonly onDidAddCapabilityType = this._onDidAddCapabilityType.event; - private readonly _onDidRemoveCapability2 = this._register(new Emitter>()); - readonly onDidRemoveCapability = this._onDidRemoveCapability2.event; - private readonly _onDidAddCapability2 = this._register(new Emitter>()); - readonly onDidAddCapability = this._onDidAddCapability2.event; + private readonly _onDidRemoveCapability = this._register(new Emitter>()); + readonly onDidRemoveCapability = this._onDidRemoveCapability.event; + private readonly _onDidAddCapability = this._register(new Emitter>()); + readonly onDidAddCapability = this._onDidAddCapability.event; get items(): IterableIterator { return this._map.keys(); @@ -26,8 +26,8 @@ export class TerminalCapabilityStore extends Disposable implements ITerminalCapa add(capability: T, impl: ITerminalCapabilityImplMap[T]) { this._map.set(capability, impl); - this._onDidAddCapability.fire(capability); - this._onDidAddCapability2.fire({ id: capability, capability: impl }); + this._onDidAddCapabilityType.fire(capability); + this._onDidAddCapability.fire({ id: capability, capability: impl }); } get(capability: T): ITerminalCapabilityImplMap[T] | undefined { @@ -41,8 +41,8 @@ export class TerminalCapabilityStore extends Disposable implements ITerminalCapa return; } this._map.delete(capability); - this._onDidRemoveCapability.fire(capability); - this._onDidAddCapability2.fire({ id: capability, capability: impl }); + this._onDidRemoveCapabilityType.fire(capability); + this._onDidAddCapability.fire({ id: capability, capability: impl }); } has(capability: TerminalCapability) { @@ -53,15 +53,15 @@ export class TerminalCapabilityStore extends Disposable implements ITerminalCapa export class TerminalCapabilityStoreMultiplexer extends Disposable implements ITerminalCapabilityStore { readonly _stores: ITerminalCapabilityStore[] = []; - private readonly _onDidRemoveCapability = this._register(new Emitter()); - readonly onDidRemoveCapabilityType = this._onDidRemoveCapability.event; - private readonly _onDidAddCapability = this._register(new Emitter()); - readonly onDidAddCapabilityType = this._onDidAddCapability.event; + private readonly _onDidRemoveCapabilityType = this._register(new Emitter()); + readonly onDidRemoveCapabilityType = this._onDidRemoveCapabilityType.event; + private readonly _onDidAddCapabilityType = this._register(new Emitter()); + readonly onDidAddCapabilityType = this._onDidAddCapabilityType.event; - private readonly _onDidRemoveCapability2 = this._register(new Emitter>()); - readonly onDidRemoveCapability = this._onDidRemoveCapability2.event; - private readonly _onDidAddCapability2 = this._register(new Emitter>()); - readonly onDidAddCapability = this._onDidAddCapability2.event; + private readonly _onDidRemoveCapability = this._register(new Emitter>()); + readonly onDidRemoveCapability = this._onDidRemoveCapability.event; + private readonly _onDidAddCapability = this._register(new Emitter>()); + readonly onDidAddCapability = this._onDidAddCapability.event; get items(): IterableIterator { return this._items(); @@ -99,12 +99,12 @@ export class TerminalCapabilityStoreMultiplexer extends Disposable implements IT add(store: ITerminalCapabilityStore) { this._stores.push(store); for (const capability of store.items) { - this._onDidAddCapability.fire(capability); - this._onDidAddCapability2.fire({ id: capability, capability: store.get(capability)! }); + this._onDidAddCapabilityType.fire(capability); + this._onDidAddCapability.fire({ id: capability, capability: store.get(capability)! }); } - store.onDidAddCapabilityType(e => this._onDidAddCapability.fire(e)); - store.onDidAddCapability(e => this._onDidAddCapability2.fire(e)); - store.onDidRemoveCapabilityType(e => this._onDidRemoveCapability.fire(e)); - store.onDidRemoveCapability(e => this._onDidRemoveCapability2.fire(e)); + store.onDidAddCapabilityType(e => this._onDidAddCapabilityType.fire(e)); + store.onDidAddCapability(e => this._onDidAddCapability.fire(e)); + store.onDidRemoveCapabilityType(e => this._onDidRemoveCapabilityType.fire(e)); + store.onDidRemoveCapability(e => this._onDidRemoveCapability.fire(e)); } } From 432b5defb4a87964e3820d023f7a1de3ce588844 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:03:46 -0700 Subject: [PATCH 227/607] Move instance event multiplexers to different file --- .../terminal/browser/terminalEvents.ts | 101 ++++++++++++++++++ .../terminal/browser/terminalService.ts | 72 +------------ 2 files changed, 106 insertions(+), 67 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/browser/terminalEvents.ts diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts b/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts new file mode 100644 index 00000000000..6a0568f7436 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts @@ -0,0 +1,101 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { Event, EventMultiplexer } from 'vs/base/common/event'; +import { DisposableMap, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { ITerminalCapabilityImplMap, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; + +export function createInstanceEventMultiplexer( + currentInstances: ITerminalInstance[], + onAddInstance: Event, + onRemoveInstance: Event, + getEvent: (instance: ITerminalInstance) => Event +): { dispose(): void; event: Event } { + const store = new DisposableStore(); + const multiplexer = store.add(new EventMultiplexer()); + const instanceListeners = store.add(new DisposableMap()); + + function addInstance(instance: ITerminalInstance) { + const listener = multiplexer.add(getEvent(instance)); + instanceListeners.set(instance, listener); + } + + // Existing instances + for (const instance of currentInstances) { + addInstance(instance); + } + + // Added instances + store.add(onAddInstance(instance => { + addInstance(instance); + })); + + // Removed instances + store.add(onRemoveInstance(instance => { + instanceListeners.deleteAndDispose(instance); + })); + + return { + dispose: () => store.dispose(), + event: multiplexer.event + }; +} + +export function createInstanceCapabilityEventMultiplexer( + currentInstances: ITerminalInstance[], + onAddInstance: Event, + onRemoveInstance: Event, + capabilityId: T, + getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event +): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { + const store = new DisposableStore(); + const multiplexer = store.add(new EventMultiplexer<{ instance: ITerminalInstance; data: K }>()); + const capabilityListeners = store.add(new DisposableMap()); + + function addCapability(instance: ITerminalInstance, capability: ITerminalCapabilityImplMap[T]) { + const listener = multiplexer.add(Event.map(getEvent(capability), data => ({ instance, data }))); + capabilityListeners.set(capability, listener); + } + + // Existing capabilities + for (const instance of currentInstances) { + const capability = instance.capabilities.get(capabilityId); + if (capability) { + addCapability(instance, capability); + } + } + + // Added capabilities + const addCapabilityMultiplexer = createInstanceEventMultiplexer( + currentInstances, + onAddInstance, + onRemoveInstance, + instance => Event.map(instance.capabilities.onDidAddCapability, changeEvent => ({ instance, changeEvent })) + ); + addCapabilityMultiplexer.event(e => { + if (e.changeEvent.id === capabilityId) { + addCapability(e.instance, e.changeEvent.capability); + } + }); + + // Removed capabilities + const removeCapabilityMultiplexer = createInstanceEventMultiplexer( + currentInstances, + onAddInstance, + onRemoveInstance, + instance => instance.capabilities.onDidRemoveCapability + ); + removeCapabilityMultiplexer.event(e => { + if (e.id === capabilityId) { + capabilityListeners.deleteAndDispose(e.capability); + } + }); + + return { + dispose: () => store.dispose(), + event: multiplexer.event + }; +} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index d24ff9c4c87..b05be0091b4 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -6,8 +6,8 @@ import * as dom from 'vs/base/browser/dom'; import { DeferredPromise, timeout } from 'vs/base/common/async'; import { debounce } from 'vs/base/common/decorators'; -import { Emitter, Event, EventMultiplexer } from 'vs/base/common/event'; -import { Disposable, DisposableMap, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -54,6 +54,7 @@ import { ITimerService } from 'vs/workbench/services/timer/browser/timerService' import { mark } from 'vs/base/common/performance'; import { DeatachedTerminal } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; import { ITerminalCapabilityImplMap, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { createInstanceCapabilityEventMultiplexer, createInstanceEventMultiplexer } from 'vs/workbench/contrib/terminal/browser/terminalEvents'; export class TerminalService extends Disposable implements ITerminalService { declare _serviceBrand: undefined; @@ -1197,74 +1198,11 @@ export class TerminalService extends Disposable implements ITerminalService { } onInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event } { - const store = new DisposableStore(); - const multiplexer = store.add(new EventMultiplexer()); - const instanceListeners = store.add(new DisposableMap()); - - function addInstance(instance: ITerminalInstance) { - const listener = multiplexer.add(getEvent(instance)); - instanceListeners.set(instance, listener); - } - - // Existing instances - for (const instance of this.instances) { - addInstance(instance); - } - - // Added instances - store.add(this.onDidCreateInstance(instance => { - addInstance(instance); - })); - - // Removed instances - store.add(this.onDidDisposeInstance(instance => { - instanceListeners.deleteAndDispose(instance); - })); - - return { - dispose: () => store.dispose(), - event: multiplexer.event - }; + return createInstanceEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, getEvent); } onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { - const store = new DisposableStore(); - const multiplexer = store.add(new EventMultiplexer<{ instance: ITerminalInstance; data: K }>()); - const capabilityListeners = store.add(new DisposableMap()); - - function addCapability(instance: ITerminalInstance, capability: ITerminalCapabilityImplMap[T]) { - const listener = multiplexer.add(Event.map(getEvent(capability), data => ({ instance, data }))); - capabilityListeners.set(capability, listener); - } - - // Existing capabilities - for (const instance of this.instances) { - const capability = instance.capabilities.get(capabilityId); - if (capability) { - addCapability(instance, capability); - } - } - - // Added capabilities - const addCapabilityMultiplexer = this.onInstanceEvent(instance => Event.map(instance.capabilities.onDidAddCapability, changeEvent => ({ instance, changeEvent }))); - addCapabilityMultiplexer.event(e => { - if (e.changeEvent.id === capabilityId) { - addCapability(e.instance, e.changeEvent.capability); - } - }); - - // Removed capabilities - const removeCapabilityMultiplexer = this.onInstanceEvent(instance => instance.capabilities.onDidRemoveCapability); - removeCapabilityMultiplexer.event(e => { - if (e.id === capabilityId) { - capabilityListeners.deleteAndDispose(e.capability); - } - }); - - return { - dispose: () => store.dispose(), - event: multiplexer.event - }; + return createInstanceCapabilityEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, capabilityId, getEvent); } } From 711c83d0d459a7e44c5a894084f4c101510a3870 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:10:32 -0700 Subject: [PATCH 228/607] Make function more generic --- .../terminal/browser/terminalEvents.ts | 39 +++++++++---------- .../terminal/browser/terminalService.ts | 4 +- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts b/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts index 6a0568f7436..51923802025 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts @@ -8,34 +8,33 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { DisposableMap, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ITerminalCapabilityImplMap, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -export function createInstanceEventMultiplexer( - currentInstances: ITerminalInstance[], - onAddInstance: Event, - onRemoveInstance: Event, - getEvent: (instance: ITerminalInstance) => Event -): { dispose(): void; event: Event } { +export function createDynamicListEventMultiplexer( + items: TItem[], + onAddItem: Event, + onRemoveItem: Event, + getEvent: (item: TItem) => Event +): { dispose(): void; event: Event } { const store = new DisposableStore(); - const multiplexer = store.add(new EventMultiplexer()); - const instanceListeners = store.add(new DisposableMap()); + const multiplexer = store.add(new EventMultiplexer()); + const itemListeners = store.add(new DisposableMap()); - function addInstance(instance: ITerminalInstance) { - const listener = multiplexer.add(getEvent(instance)); - instanceListeners.set(instance, listener); + function addInstance(instance: TItem) { + itemListeners.set(instance, multiplexer.add(getEvent(instance))); } - // Existing instances - for (const instance of currentInstances) { + // Existing items + for (const instance of items) { addInstance(instance); } - // Added instances - store.add(onAddInstance(instance => { + // Added items + store.add(onAddItem(instance => { addInstance(instance); })); - // Removed instances - store.add(onRemoveInstance(instance => { - instanceListeners.deleteAndDispose(instance); + // Removed items + store.add(onRemoveItem(instance => { + itemListeners.deleteAndDispose(instance); })); return { @@ -69,7 +68,7 @@ export function createInstanceCapabilityEventMultiplexer(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event } { - return createInstanceEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, getEvent); + return createDynamicListEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, getEvent); } onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { From 694459fa74c385e8ac01e46ab80b14276087d3eb Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 10:38:30 -0700 Subject: [PATCH 229/607] Change function into a class --- src/vs/base/common/event.ts | 46 ++++++++++++++++++- .../contrib/terminal/browser/terminal.ts | 6 +-- .../terminal/browser/terminalEvents.ts | 43 ++--------------- .../terminal/browser/terminalService.ts | 10 ++-- .../test/browser/terminalEvents.test.ts | 23 ++++++++++ 5 files changed, 80 insertions(+), 48 deletions(-) create mode 100644 src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index ae7f8701dca..52a4b2d20f6 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -6,7 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError } from 'vs/base/common/errors'; import { once as onceFn } from 'vs/base/common/functional'; -import { combinedDisposable, Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { IObservable, IObserver } from 'vs/base/common/observable'; import { StopWatch } from 'vs/base/common/stopwatch'; @@ -1429,6 +1429,50 @@ export class EventMultiplexer implements IDisposable { } } +export interface IDynamicListEventMultiplexer extends IDisposable { + readonly event: Event; +} +export class DynamicListEventMultiplexer implements IDynamicListEventMultiplexer { + private readonly _store = new DisposableStore(); + + readonly event: Event; + + constructor( + items: TItem[], + onAddItem: Event, + onRemoveItem: Event, + getEvent: (item: TItem) => Event + ) { + const multiplexer = this._store.add(new EventMultiplexer()); + const itemListeners = this._store.add(new DisposableMap()); + + function addItem(instance: TItem) { + itemListeners.set(instance, multiplexer.add(getEvent(instance))); + } + + // Existing items + for (const instance of items) { + addItem(instance); + } + + // Added items + this._store.add(onAddItem(instance => { + addItem(instance); + })); + + // Removed items + this._store.add(onRemoveItem(instance => { + itemListeners.deleteAndDispose(instance); + })); + + this.event = multiplexer.event; + } + + dispose() { + this._store.dispose(); + } +} + /** * The EventBufferer is useful in situations in which you want * to delay firing your events during some code. diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index ec1a5df552f..e02fbf99e5e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -6,7 +6,7 @@ import { IDimension } from 'vs/base/browser/dom'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { Color } from 'vs/base/common/color'; -import { Event } from 'vs/base/common/event'; +import { Event, IDynamicListEventMultiplexer } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { OperatingSystem } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -269,7 +269,7 @@ export interface ITerminalService extends ITerminalInstanceHost { * instances and removing old instances as needed. * @param getEvent Maps the instance to the event. */ - onInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event }; + onInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): IDynamicListEventMultiplexer; /** * Creates a capability event listener that listens to capabilities on all instances, @@ -277,7 +277,7 @@ export interface ITerminalService extends ITerminalInstanceHost { * @param capabilityId The capability type to listen to an event on. * @param getEvent Maps the capability to the event. */ - onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> }; + onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): IDynamicListEventMultiplexer<{ instance: ITerminalInstance; data: K }>; } export class TerminalLinkQuickPickEvent extends MouseEvent { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts b/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts index 51923802025..9f1630864a8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalEvents.ts @@ -4,52 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { Event, EventMultiplexer } from 'vs/base/common/event'; +import { DynamicListEventMultiplexer, Event, EventMultiplexer, IDynamicListEventMultiplexer } from 'vs/base/common/event'; import { DisposableMap, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ITerminalCapabilityImplMap, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -export function createDynamicListEventMultiplexer( - items: TItem[], - onAddItem: Event, - onRemoveItem: Event, - getEvent: (item: TItem) => Event -): { dispose(): void; event: Event } { - const store = new DisposableStore(); - const multiplexer = store.add(new EventMultiplexer()); - const itemListeners = store.add(new DisposableMap()); - - function addInstance(instance: TItem) { - itemListeners.set(instance, multiplexer.add(getEvent(instance))); - } - - // Existing items - for (const instance of items) { - addInstance(instance); - } - - // Added items - store.add(onAddItem(instance => { - addInstance(instance); - })); - - // Removed items - store.add(onRemoveItem(instance => { - itemListeners.deleteAndDispose(instance); - })); - - return { - dispose: () => store.dispose(), - event: multiplexer.event - }; -} - export function createInstanceCapabilityEventMultiplexer( currentInstances: ITerminalInstance[], onAddInstance: Event, onRemoveInstance: Event, capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event -): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { +): IDynamicListEventMultiplexer<{ instance: ITerminalInstance; data: K }> { const store = new DisposableStore(); const multiplexer = store.add(new EventMultiplexer<{ instance: ITerminalInstance; data: K }>()); const capabilityListeners = store.add(new DisposableMap()); @@ -68,7 +33,7 @@ export function createInstanceCapabilityEventMultiplexer(getEvent: (instance: ITerminalInstance) => Event): { dispose(): void; event: Event } { - return createDynamicListEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, getEvent); + onInstanceEvent(getEvent: (instance: ITerminalInstance) => Event): IDynamicListEventMultiplexer { + return new DynamicListEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, getEvent); } - onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): { dispose(): void; event: Event<{ instance: ITerminalInstance; data: K }> } { + onInstanceCapabilityEvent(capabilityId: T, getEvent: (capability: ITerminalCapabilityImplMap[T]) => Event): IDynamicListEventMultiplexer<{ instance: ITerminalInstance; data: K }> { return createInstanceCapabilityEventMultiplexer(this.instances, this.onDidCreateInstance, this.onDidDisposeInstance, capabilityId, getEvent); } } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts new file mode 100644 index 00000000000..261f207ad16 --- /dev/null +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DynamicListEventMultiplexer } from 'vs/base/common/event'; + +suite.only('terminalEvents', () => { + suite('createInstanceEventMultiplexer', () => { + // let instance + test('should fire events for current instances', () => { + // const e = new DynamicListEventMultiplexer([] + }); + test('should fire events for added instances', () => { + }); + test('should NOT fire events for removed instances', () => { + }); + }); + suite('createInstanceCapabilityEventMultiplexer', () => { + test('', () => { + }); + }); +}); From 3a1364baf8cfa860840fd05e27cf306dc6d2af13 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Fri, 25 Aug 2023 11:20:55 -0700 Subject: [PATCH 230/607] Search view not showing warnings (#191333) * Search view not showing warnings Fixes #191263 * cleanup --- .../contrib/search/browser/searchModel.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 0d8c8ec06c0..f8261d8871b 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -2102,13 +2102,18 @@ export class SearchModel extends Disposable { this.telemetryService.publicLog('searchResultsFirstRender', { duration: Date.now() - start }); }); - asyncResults.then( - value => this.onSearchCompleted(value, Date.now() - start, searchInstanceID), - e => this.onSearchError(e, Date.now() - start)); try { return { - asyncResults: asyncResults, - syncResults: syncResults + asyncResults: asyncResults.then( + value => { + this.onSearchCompleted(value, Date.now() - start, searchInstanceID); + return value; + }, + e => { + this.onSearchError(e, Date.now() - start); + throw e; + }), + syncResults }; } finally { /* __GDPR__ From 29661cb547fed7f6253d9bcb3699ae4379fd63d9 Mon Sep 17 00:00:00 2001 From: hsfzxjy Date: Sat, 26 Aug 2023 03:11:41 +0800 Subject: [PATCH 231/607] Add context key notebookEditorCursorAtLineBoundary (#187679) Add context notebookEditorCursorAtLineBoundary --- .../notebook/browser/notebookBrowser.ts | 7 +++++ .../notebook/browser/view/notebookCellList.ts | 22 ++++++++++++-- .../browser/viewModel/baseCellViewModel.ts | 29 ++++++++++++++++++- .../contrib/notebook/common/notebookCommon.ts | 1 + 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 587b8619587..4f1d98b86bb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -805,6 +805,13 @@ export enum CursorAtBoundary { Both } +export enum CursorAtLineBoundary { + None, + Start, + End, + Both +} + export function getNotebookEditorFromEditorPane(editorPane?: IEditorPane): INotebookEditor | undefined { if (!editorPane) { return; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 0da4dd25742..5700dc24c05 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -18,9 +18,9 @@ import { PrefixSumComputer } from 'vs/editor/common/model/prefixSumComputer'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IListService, IWorkbenchListOptions, WorkbenchList } from 'vs/platform/list/browser/listService'; -import { CursorAtBoundary, ICellViewModel, CellEditState, CellFocusMode, ICellOutputViewModel, CellRevealType, CellRevealSyncType, CellRevealRangeType } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CursorAtBoundary, ICellViewModel, CellEditState, CellFocusMode, ICellOutputViewModel, CellRevealType, CellRevealSyncType, CellRevealRangeType, CursorAtLineBoundary } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModel, NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; -import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { diff, NOTEBOOK_EDITOR_CURSOR_BOUNDARY, CellKind, SelectionStateType, NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange, cellRangesToIndexes, reduceCellRanges, cellRangesEqual } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { NOTEBOOK_CELL_LIST_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { clamp } from 'vs/base/common/numbers'; @@ -172,6 +172,9 @@ export class NotebookCellList extends WorkbenchList implements ID const notebookEditorCursorAtBoundaryContext = NOTEBOOK_EDITOR_CURSOR_BOUNDARY.bindTo(contextKeyService); notebookEditorCursorAtBoundaryContext.set('none'); + const notebookEditorCursorAtLineBoundaryContext = NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY.bindTo(contextKeyService); + notebookEditorCursorAtLineBoundaryContext.set('none'); + const cursorSelectionListener = this._localDisposableStore.add(new MutableDisposable()); const textEditorAttachListener = this._localDisposableStore.add(new MutableDisposable()); @@ -191,6 +194,21 @@ export class NotebookCellList extends WorkbenchList implements ID break; } + switch (element.cursorAtLineBoundary()) { + case CursorAtLineBoundary.Both: + notebookEditorCursorAtLineBoundaryContext.set('both'); + break; + case CursorAtLineBoundary.Start: + notebookEditorCursorAtLineBoundaryContext.set('start'); + break; + case CursorAtLineBoundary.End: + notebookEditorCursorAtLineBoundaryContext.set('end'); + break; + default: + notebookEditorCursorAtLineBoundaryContext.set('none'); + break; + } + return; }; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 148bc0af9ef..0b147c7f7c0 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -18,7 +18,7 @@ import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/se import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { IWordWrapTransientState, readTransientState, writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; -import { CellEditState, CellFocusMode, CursorAtBoundary, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, CursorAtBoundary, CursorAtLineBoundary, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; @@ -499,6 +499,33 @@ export abstract class BaseCellViewModel extends Disposable { return this._textEditor.getTopForPosition(position.lineNumber, position.column) + editorPadding.top; } + cursorAtLineBoundary(): CursorAtLineBoundary { + if (!this._textEditor || !this.textModel || !this._textEditor.hasTextFocus()) { + return CursorAtLineBoundary.None; + } + + const selection = this._textEditor.getSelection(); + + if (!selection || !selection.isEmpty()) { + return CursorAtLineBoundary.None; + } + + const currentLineLength = this.textModel.getLineLength(selection.startLineNumber); + + if (currentLineLength === 0) { + return CursorAtLineBoundary.Both; + } + + switch (selection.startColumn) { + case 1: + return CursorAtLineBoundary.Start; + case currentLineLength + 1: + return CursorAtLineBoundary.End; + default: + return CursorAtLineBoundary.None; + } + } + cursorAtBoundary(): CursorAtBoundary { if (!this._textEditor) { return CursorAtBoundary.None; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 2182e98f021..343e77b210a 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -780,6 +780,7 @@ export interface ICellEditorViewState { export const NOTEBOOK_EDITOR_CURSOR_BOUNDARY = new RawContextKey<'none' | 'top' | 'bottom' | 'both'>('notebookEditorCursorAtBoundary', 'none'); +export const NOTEBOOK_EDITOR_CURSOR_LINE_BOUNDARY = new RawContextKey<'none' | 'start' | 'end' | 'both'>('notebookEditorCursorAtLineBoundary', 'none'); export interface INotebookLoadOptions { /** From ca462c038120853f8e6f13d8580d1d3ef34b5860 Mon Sep 17 00:00:00 2001 From: Ryosuke Hirakawa <38846793+hrkw00@users.noreply.github.com> Date: Sat, 26 Aug 2023 04:48:44 +0900 Subject: [PATCH 232/607] Fix comment typo in standaloneLanguages.ts (#189449) * fix * Revert "fix" This reverts commit 75c1173714f29f55d2ca18684b1768251edd489e. * fix * update monaco.d.ts --- src/vs/editor/standalone/browser/standaloneLanguages.ts | 2 +- src/vs/monaco.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 939d3391034..ec191dfec13 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -70,7 +70,7 @@ export function onLanguage(languageId: string, callback: () => void): IDisposabl /** * An event emitted when a language is associated for the first time with a text model or - * whena language is encountered during the tokenization of another language. + * when a language is encountered during the tokenization of another language. * @event */ export function onLanguageEncountered(languageId: string, callback: () => void): IDisposable { diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 2d030c3fa23..2b0ba7176d8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -6257,7 +6257,7 @@ declare namespace monaco.languages { /** * An event emitted when a language is associated for the first time with a text model or - * whena language is encountered during the tokenization of another language. + * when a language is encountered during the tokenization of another language. * @event */ export function onLanguageEncountered(languageId: string, callback: () => void): IDisposable; From a5377b7a09f4693102eb8471e728f391fab32c7b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 12:53:42 -0700 Subject: [PATCH 233/607] Add DynamicListEventMultiplexer unit tests --- src/vs/base/test/common/event.test.ts | 48 ++++++++++++++++++- .../test/browser/terminalEvents.test.ts | 23 --------- 2 files changed, 47 insertions(+), 24 deletions(-) delete mode 100644 src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 84c0d21dbb7..8f14d7d47cd 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -7,7 +7,7 @@ import { stub } from 'sinon'; import { timeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { errorHandler, setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import { AsyncEmitter, DebounceEmitter, Emitter, Event, EventBufferer, EventMultiplexer, IWaitUntil, MicrotaskEmitter, PauseableEmitter, Relay, createEventDeliveryQueue } from 'vs/base/common/event'; +import { AsyncEmitter, DebounceEmitter, DynamicListEventMultiplexer, Emitter, Event, EventBufferer, EventMultiplexer, IWaitUntil, MicrotaskEmitter, PauseableEmitter, Relay, createEventDeliveryQueue } from 'vs/base/common/event'; import { DisposableStore, IDisposable, isDisposable, setDisposableTracker, toDisposable } from 'vs/base/common/lifecycle'; import { observableValue, transaction } from 'vs/base/common/observable'; import { MicrotaskDelay } from 'vs/base/common/symbols'; @@ -1065,6 +1065,52 @@ suite('Event utils', () => { }); }); + suite('DynamicListEventMultiplexer', () => { + const recordedEvents: number[] = []; + const addEmitter = new Emitter(); + const removeEmitter = new Emitter(); + class TestItem { + readonly onTestEventEmitter = new Emitter(); + readonly onTestEvent = this.onTestEventEmitter.event; + } + let items: TestItem[]; + let m: DynamicListEventMultiplexer; + setup(() => { + items = [new TestItem(), new TestItem()]; + for (const [i, item] of items.entries()) { + item.onTestEvent(e => `${i}:${e}`); + } + m = new DynamicListEventMultiplexer(items, addEmitter.event, removeEmitter.event, e => e.onTestEvent); + m.event(e => recordedEvents.push(e)); + recordedEvents.length = 0; + }); + teardown(() => m.dispose()); + test('should fire events for initial items', () => { + items[0].onTestEventEmitter.fire(1); + items[1].onTestEventEmitter.fire(2); + items[0].onTestEventEmitter.fire(3); + items[1].onTestEventEmitter.fire(4); + assert.deepStrictEqual(recordedEvents, [1, 2, 3, 4]); + }); + test('should fire events for added items', () => { + const addedItem = new TestItem(); + addEmitter.fire(addedItem); + addedItem.onTestEventEmitter.fire(1); + items[0].onTestEventEmitter.fire(2); + items[1].onTestEventEmitter.fire(3); + addedItem.onTestEventEmitter.fire(4); + assert.deepStrictEqual(recordedEvents, [1, 2, 3, 4]); + }); + test('should not fire events for removed items', () => { + removeEmitter.fire(items[0]); + items[0].onTestEventEmitter.fire(1); + items[1].onTestEventEmitter.fire(2); + items[0].onTestEventEmitter.fire(3); + items[1].onTestEventEmitter.fire(4); + assert.deepStrictEqual(recordedEvents, [2, 4]); + }); + }); + test('latch', () => { const emitter = new Emitter(); const event = Event.latch(emitter.event); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts deleted file mode 100644 index 261f207ad16..00000000000 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalEvents.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { DynamicListEventMultiplexer } from 'vs/base/common/event'; - -suite.only('terminalEvents', () => { - suite('createInstanceEventMultiplexer', () => { - // let instance - test('should fire events for current instances', () => { - // const e = new DynamicListEventMultiplexer([] - }); - test('should fire events for added instances', () => { - }); - test('should NOT fire events for removed instances', () => { - }); - }); - suite('createInstanceCapabilityEventMultiplexer', () => { - test('', () => { - }); - }); -}); From 2672173f732e4cb513e029fe224538916274687a Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Fri, 25 Aug 2023 13:19:09 -0700 Subject: [PATCH 234/607] Tuning action widget heading styles (#191336) tuning headers color and style to match quick text --- .../contrib/codeAction/browser/codeActionMenu.ts | 14 +++++++------- .../platform/actionWidget/browser/actionWidget.css | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts index fc33e04fdd0..8108d979327 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts @@ -22,13 +22,13 @@ interface ActionGroup { const uncategorizedCodeActionGroup = Object.freeze({ kind: CodeActionKind.Empty, title: localize('codeAction.widget.id.more', 'More Actions...') }); const codeActionGroups = Object.freeze([ - { kind: CodeActionKind.QuickFix, title: localize('codeAction.widget.id.quickfix', 'Quick Fix:') }, - { kind: CodeActionKind.RefactorExtract, title: localize('codeAction.widget.id.extract', 'Extract:'), icon: Codicon.wrench }, - { kind: CodeActionKind.RefactorInline, title: localize('codeAction.widget.id.inline', 'Inline:'), icon: Codicon.wrench }, - { kind: CodeActionKind.RefactorRewrite, title: localize('codeAction.widget.id.convert', 'Rewrite:'), icon: Codicon.wrench }, - { kind: CodeActionKind.RefactorMove, title: localize('codeAction.widget.id.move', 'Move:'), icon: Codicon.wrench }, - { kind: CodeActionKind.SurroundWith, title: localize('codeAction.widget.id.surround', 'Surround With:'), icon: Codicon.symbolSnippet }, - { kind: CodeActionKind.Source, title: localize('codeAction.widget.id.source', 'Source Action:'), icon: Codicon.symbolFile }, + { kind: CodeActionKind.QuickFix, title: localize('codeAction.widget.id.quickfix', 'Quick Fix') }, + { kind: CodeActionKind.RefactorExtract, title: localize('codeAction.widget.id.extract', 'Extract'), icon: Codicon.wrench }, + { kind: CodeActionKind.RefactorInline, title: localize('codeAction.widget.id.inline', 'Inline'), icon: Codicon.wrench }, + { kind: CodeActionKind.RefactorRewrite, title: localize('codeAction.widget.id.convert', 'Rewrite'), icon: Codicon.wrench }, + { kind: CodeActionKind.RefactorMove, title: localize('codeAction.widget.id.move', 'Move'), icon: Codicon.wrench }, + { kind: CodeActionKind.SurroundWith, title: localize('codeAction.widget.id.surround', 'Surround With'), icon: Codicon.symbolSnippet }, + { kind: CodeActionKind.Source, title: localize('codeAction.widget.id.source', 'Source Action'), icon: Codicon.symbolFile }, uncategorizedCodeActionGroup, ]); diff --git a/src/vs/platform/actionWidget/browser/actionWidget.css b/src/vs/platform/actionWidget/browser/actionWidget.css index 539af6ca265..91c30bdab12 100644 --- a/src/vs/platform/actionWidget/browser/actionWidget.css +++ b/src/vs/platform/actionWidget/browser/actionWidget.css @@ -69,7 +69,7 @@ } .action-widget .monaco-list-row.group-header { - color: var(--vscode-pickerGroup-foreground) !important; + color: var(--vscode-descriptionForeground) !important; font-weight: 600; } From fd7e42af888e19b709da41d78ae3c8aeb95c51f2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 25 Aug 2023 13:48:34 -0700 Subject: [PATCH 235/607] Fix unit test --- .../terminalCapabilityStore.test.ts | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts index 6700e44426f..54c1489cb99 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts @@ -24,28 +24,28 @@ suite('TerminalCapabilityStore', () => { test('should fire events when capabilities are added', () => { assertEvents(addEvents, []); - store.add(TerminalCapability.CwdDetection, null!); + store.add(TerminalCapability.CwdDetection, {} as any); assertEvents(addEvents, [TerminalCapability.CwdDetection]); }); test('should fire events when capabilities are removed', async () => { assertEvents(removeEvents, []); - store.add(TerminalCapability.CwdDetection, null!); + store.add(TerminalCapability.CwdDetection, {} as any); assertEvents(removeEvents, []); store.remove(TerminalCapability.CwdDetection); assertEvents(removeEvents, [TerminalCapability.CwdDetection]); }); test('has should return whether a capability is present', () => { deepStrictEqual(store.has(TerminalCapability.CwdDetection), false); - store.add(TerminalCapability.CwdDetection, null!); + store.add(TerminalCapability.CwdDetection, {} as any); deepStrictEqual(store.has(TerminalCapability.CwdDetection), true); store.remove(TerminalCapability.CwdDetection); deepStrictEqual(store.has(TerminalCapability.CwdDetection), false); }); test('items should reflect current state', () => { deepStrictEqual(Array.from(store.items), []); - store.add(TerminalCapability.CwdDetection, null!); + store.add(TerminalCapability.CwdDetection, {} as any); deepStrictEqual(Array.from(store.items), [TerminalCapability.CwdDetection]); - store.add(TerminalCapability.NaiveCwdDetection, null!); + store.add(TerminalCapability.NaiveCwdDetection, {} as any); deepStrictEqual(Array.from(store.items), [TerminalCapability.CwdDetection, TerminalCapability.NaiveCwdDetection]); store.remove(TerminalCapability.CwdDetection); deepStrictEqual(Array.from(store.items), [TerminalCapability.NaiveCwdDetection]); @@ -75,17 +75,17 @@ suite('TerminalCapabilityStoreMultiplexer', () => { assertEvents(addEvents, []); multiplexer.add(store1); multiplexer.add(store2); - store1.add(TerminalCapability.CwdDetection, null!); + store1.add(TerminalCapability.CwdDetection, {} as any); assertEvents(addEvents, [TerminalCapability.CwdDetection]); - store2.add(TerminalCapability.NaiveCwdDetection, null!); + store2.add(TerminalCapability.NaiveCwdDetection, {} as any); assertEvents(addEvents, [TerminalCapability.NaiveCwdDetection]); }); test('should fire events when capabilities are disabled', async () => { assertEvents(removeEvents, []); multiplexer.add(store1); multiplexer.add(store2); - store1.add(TerminalCapability.CwdDetection, null!); - store2.add(TerminalCapability.NaiveCwdDetection, null!); + store1.add(TerminalCapability.CwdDetection, {} as any); + store2.add(TerminalCapability.NaiveCwdDetection, {} as any); assertEvents(removeEvents, []); store1.remove(TerminalCapability.CwdDetection); assertEvents(removeEvents, [TerminalCapability.CwdDetection]); @@ -94,9 +94,9 @@ suite('TerminalCapabilityStoreMultiplexer', () => { }); test('should fire events when stores are added', async () => { assertEvents(addEvents, []); - store1.add(TerminalCapability.CwdDetection, null!); + store1.add(TerminalCapability.CwdDetection, {} as any); assertEvents(addEvents, []); - store2.add(TerminalCapability.NaiveCwdDetection, null!); + store2.add(TerminalCapability.NaiveCwdDetection, {} as any); multiplexer.add(store1); multiplexer.add(store2); assertEvents(addEvents, [TerminalCapability.CwdDetection, TerminalCapability.NaiveCwdDetection]); @@ -105,10 +105,10 @@ suite('TerminalCapabilityStoreMultiplexer', () => { deepStrictEqual(Array.from(multiplexer.items).sort(), [].sort()); multiplexer.add(store1); multiplexer.add(store2); - store1.add(TerminalCapability.CwdDetection, null!); + store1.add(TerminalCapability.CwdDetection, {} as any); deepStrictEqual(Array.from(multiplexer.items).sort(), [TerminalCapability.CwdDetection].sort()); - store1.add(TerminalCapability.CommandDetection, null!); - store2.add(TerminalCapability.NaiveCwdDetection, null!); + store1.add(TerminalCapability.CommandDetection, {} as any); + store2.add(TerminalCapability.NaiveCwdDetection, {} as any); deepStrictEqual(Array.from(multiplexer.items).sort(), [TerminalCapability.CwdDetection, TerminalCapability.CommandDetection, TerminalCapability.NaiveCwdDetection].sort()); store2.remove(TerminalCapability.NaiveCwdDetection); deepStrictEqual(Array.from(multiplexer.items).sort(), [TerminalCapability.CwdDetection, TerminalCapability.CommandDetection].sort()); @@ -116,7 +116,7 @@ suite('TerminalCapabilityStoreMultiplexer', () => { test('has should return whether a capability is present', () => { deepStrictEqual(multiplexer.has(TerminalCapability.CwdDetection), false); multiplexer.add(store1); - store1.add(TerminalCapability.CwdDetection, null!); + store1.add(TerminalCapability.CwdDetection, {} as any); deepStrictEqual(multiplexer.has(TerminalCapability.CwdDetection), true); store1.remove(TerminalCapability.CwdDetection); deepStrictEqual(multiplexer.has(TerminalCapability.CwdDetection), false); From 71fd986efb0c4e1d4c79ab2f0dc7d24a975fe2e3 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Fri, 25 Aug 2023 14:17:58 -0700 Subject: [PATCH 236/607] Fixes rendering of response that is below the fold (#191337) Steps: 1. ask `reverse a linked list` 2. scroll up right after hitting ENTER 3. wait for response to finish 4. scroll down the view should grow to the response. --- .../contrib/chat/browser/chatListRenderer.ts | 20 +++++++++++++++++-- .../contrib/chat/browser/chatWidget.ts | 5 ++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index e6ec6bd3ae8..1208e6134c3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -354,7 +354,15 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { + disposable.dispose(); + this._onDidChangeItemHeight.fire({ element, height: newHeight }); + })); + } } private renderWelcomeMessage(element: IChatWelcomeMessageViewModel, templateData: IChatListItemTemplate) { @@ -381,7 +389,15 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { + disposable.dispose(); + this._onDidChangeItemHeight.fire({ element, height: newHeight }); + })); + } } /** diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 053278d5af6..7d61828c403 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -530,7 +530,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this._register(this.renderer.onDidChangeItemHeight(() => this.layoutDynamicChatTreeItemMode())); } - layoutDynamicChatTreeItemMode(allowRecurse = true): void { + layoutDynamicChatTreeItemMode(): void { if (!this.viewModel) { return; } @@ -554,10 +554,9 @@ export class ChatWidget extends Disposable implements IChatWidget { this.container.offsetWidth ); - if (needsRerender && allowRecurse) { + if (needsRerender) { // TODO: figure out a better place to reveal the last element revealLastElement(this.tree); - this.layoutDynamicChatTreeItemMode(false); } } From fea45bae6216af9494121f9ef2d76ee787251000 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 25 Aug 2023 23:29:17 -0700 Subject: [PATCH 237/607] Add response variables (#191349) * Add response variables * Fix test * add comment --- .../api/browser/mainThreadChatVariables.ts | 2 +- .../chat/browser/actions/chatTitleActions.ts | 38 +++++++++++++++++++ .../contrib/chat/browser/chat.contribution.ts | 1 + .../browser/contrib/chatHistoryVariables.ts | 34 +++++++++++++++++ .../browser/contrib/chatInputEditorContrib.ts | 38 +++++++++++++------ .../contrib/chat/common/chatServiceImpl.ts | 2 +- .../contrib/chat/common/chatVariables.ts | 37 +++++++++++------- .../chat/test/common/chatVariables.test.ts | 12 +++--- 8 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/browser/contrib/chatHistoryVariables.ts diff --git a/src/vs/workbench/api/browser/mainThreadChatVariables.ts b/src/vs/workbench/api/browser/mainThreadChatVariables.ts index bc7fff86be1..2f731a5b62f 100644 --- a/src/vs/workbench/api/browser/mainThreadChatVariables.ts +++ b/src/vs/workbench/api/browser/mainThreadChatVariables.ts @@ -26,7 +26,7 @@ export class MainThreadChatSlashCommands implements MainThreadChatVariablesShape } $registerVariable(handle: number, data: IChatVariableData): void { - const registration = this._chatVariablesService.registerVariable(data, (messageText, token) => { + const registration = this._chatVariablesService.registerVariable(data, (messageText, _arg, _model, token) => { return this._proxy.$resolveVariable(handle, messageText, token); }); this._variables.set(handle, registration); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index 19bd054e7b8..1b26cbba32b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -104,6 +104,44 @@ export function registerChatTitleActions() { } }); + registerAction2(class MentionAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.chat.mention', + title: { + value: localize('interactive.mention.label', "Mention"), + original: 'Mention' + }, + f1: false, + category: CHAT_CATEGORY, + icon: Codicon.add, + menu: { + id: MenuId.ChatMessageTitle, + group: 'navigation', + order: 3, + when: CONTEXT_RESPONSE + } + }); + } + + run(accessor: ServicesAccessor, ...args: any[]) { + const item = args[0]; + if (!isResponseVM(item)) { + return; + } + + const chatWidgetService = accessor.get(IChatWidgetService); + const widget = chatWidgetService.lastFocusedWidget!; + const num = widget.viewModel!.getItems() + .filter(isResponseVM) + .indexOf(item) + 1; + widget.inputEditor.setValue(`${widget.inputEditor.getValue()} @response:${num} `); + const lastLine = widget.inputEditor.getModel()!.getLineCount(); + const lastCol = widget.inputEditor.getModel()!.getLineLength(lastLine); + widget.inputEditor.setSelection({ startColumn: lastCol, endColumn: lastCol, startLineNumber: lastLine, endLineNumber: lastLine }); + } + }); + registerAction2(class InsertToNotebookAction extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 9e6bf370a27..9e6ad4404f1 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -28,6 +28,7 @@ import { ChatEditor, IChatEditorOptions } from 'vs/workbench/contrib/chat/browse import { ChatEditorInput, ChatEditorInputSerializer } from 'vs/workbench/contrib/chat/browser/chatEditorInput'; import { ChatWidgetService } from 'vs/workbench/contrib/chat/browser/chatWidget'; import 'vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib'; +import 'vs/workbench/contrib/chat/browser/contrib/chatHistoryVariables'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { ChatService } from 'vs/workbench/contrib/chat/common/chatServiceImpl'; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatHistoryVariables.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatHistoryVariables.ts new file mode 100644 index 00000000000..5aa6815b698 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatHistoryVariables.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; + +class ChatHistoryVariables extends Disposable { + constructor( + @IChatVariablesService chatVariablesService: IChatVariablesService, + ) { + super(); + + this._register(chatVariablesService.registerVariable({ name: 'response', description: '', canTakeArgument: true, hidden: true }, async (message, arg, model, token) => { + if (!arg) { + return undefined; + } + + const responseNum = parseInt(arg, 10); + const response = model.getRequests()[responseNum - 1].response; + if (!response) { + return undefined; + } + + return [{ level: 'full', value: response.response.asString() }]; + })); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ChatHistoryVariables, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 859d2bfbaca..9ef0f6b1601 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -26,9 +26,9 @@ import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workbench/contrib/chat/common/chatColors'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; - const decorationDescription = 'chat'; const slashCommandPlaceholderDecorationType = 'chat-session-detail'; const slashCommandTextDecorationType = 'chat-session-text'; @@ -176,7 +176,7 @@ class InputEditorDecorations extends Disposable { } // const variables = this.chatVariablesService.getVariables(); - const variableReg = /(^|\s)@(\w+)(?=(\s|$))/ig; + const variableReg = /(^|\s)@(\w+)(:\d+)?(?=(\s|$))/ig; let match: RegExpMatchArray | null; const varDecorations: IDecorationOptions[] = []; while (match = variableReg.exec(inputValue)) { @@ -309,17 +309,31 @@ class VariableCompletions extends Disposable { replace = new Range(position.lineNumber, varWord.startColumn, position.lineNumber, varWord.endColumn); } + const history = widget.viewModel!.getItems() + .filter(isResponseVM); + + // TODO@roblourens work out a real API for this- maybe it can be part of the two-step flow that @file will probably use + const historyItems = history.map((h, i): CompletionItem => ({ + label: `@response:${i + 1}`, + detail: h.response.asString(), + insertText: `@response:${String(i + 1).padStart(String(history.length).length, '0')} `, + kind: CompletionItemKind.Text, + range: { insert, replace }, + })); + + const variableItems = Array.from(this.chatVariablesService.getVariables()).map(v => { + const withAt = `@${v.name}`; + return { + label: withAt, + range: { insert, replace }, + insertText: withAt + ' ', + detail: v.description, + kind: CompletionItemKind.Text, // The icons are disabled here anyway, + }; + }); + return { - suggestions: Array.from(this.chatVariablesService.getVariables()).map(v => { - const withAt = `@${v.name}`; - return { - label: withAt, - range: { insert, replace }, - insertText: withAt + ' ', - detail: v.description, - kind: CompletionItemKind.Text, // The icons are disabled here anyway, - }; - }) + suggestions: [...variableItems, ...historyItems] }; } })); diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index bd759fd4c94..f3396c2053e 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -508,7 +508,7 @@ export class ChatService extends Disposable implements IChatService { }; if (typeof request.message === 'string') { - request.variables = await this.chatVariablesService.resolveVariables(request.message, token); + request.variables = await this.chatVariablesService.resolveVariables(request.message, model, token); } rawResponse = await provider.provideReply(request, progressCallback, token); diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index f8fe3874ee9..953888691ef 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -8,10 +8,13 @@ import { onUnexpectedExternalError } from 'vs/base/common/errors'; import { Iterable } from 'vs/base/common/iterator'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; export interface IChatVariableData { name: string; description: string; + hidden?: boolean; + canTakeArgument?: boolean; } export interface IChatRequestVariableValue { @@ -22,7 +25,7 @@ export interface IChatRequestVariableValue { export interface IChatVariableResolver { // TODO should we spec "zoom level" - (messageText: string, token: CancellationToken): Promise; + (messageText: string, arg: string | undefined, model: IChatModel, token: CancellationToken): Promise; } export const IChatVariablesService = createDecorator('IChatVariablesService'); @@ -35,35 +38,42 @@ export interface IChatVariablesService { /** * Resolves all variables that occur in `prompt` */ - resolveVariables(prompt: string, token: CancellationToken): Promise>; + resolveVariables(prompt: string, model: IChatModel, token: CancellationToken): Promise>; } -type ChatData = [data: IChatVariableData, resolver: IChatVariableResolver]; +interface IChatData { + data: IChatVariableData; + resolver: IChatVariableResolver; +} export class ChatVariablesService implements IChatVariablesService { declare _serviceBrand: undefined; - private _resolver = new Map(); + private _resolver = new Map(); constructor() { } - async resolveVariables(prompt: string, token: CancellationToken): Promise> { + async resolveVariables(prompt: string, model: IChatModel, token: CancellationToken): Promise> { const resolvedVariables: Record = {}; const jobs: Promise[] = []; - const regex = /(^|\s)@(\w+)(\s|$)/ig; + const regex = /(^|\s)@(\w+)(:\w+)?(\s|$)/ig; let match: RegExpMatchArray | null; while (match = regex.exec(prompt)) { const candidate = match[2]; const data = this._resolver.get(candidate.toLowerCase()); if (data) { - jobs.push(data[1](prompt, token).then(value => { - if (value) { - resolvedVariables[candidate] = value; - } - }).catch(onUnexpectedExternalError)); + const arg = match[3]; + if (!arg || data.data.canTakeArgument) { + const argWithoutColon = arg?.slice(1); + jobs.push(data.resolver(prompt, argWithoutColon, model, token).then(value => { + if (value) { + resolvedVariables[candidate + (arg ?? '')] = value; + } + }).catch(onUnexpectedExternalError)); + } } } @@ -73,7 +83,8 @@ export class ChatVariablesService implements IChatVariablesService { } getVariables(): Iterable> { - return Iterable.map(this._resolver.values(), data => data[0]); + const all = Iterable.map(this._resolver.values(), data => data.data); + return Iterable.filter(all, data => !data.hidden); } registerVariable(data: IChatVariableData, resolver: IChatVariableResolver): IDisposable { @@ -81,7 +92,7 @@ export class ChatVariablesService implements IChatVariablesService { if (this._resolver.has(key)) { throw new Error(`A chat variable with the name '${data.name}' already exists.`); } - this._resolver.set(key, [data, resolver]); + this._resolver.set(key, { data, resolver }); return toDisposable(() => { this._resolver.delete(key); }); diff --git a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts index a24ab711a95..ac6644efe83 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts @@ -21,32 +21,32 @@ suite('ChatVariables', function () { service.registerVariable({ name: 'far', description: 'boo' }, async () => ([{ level: 'full', value: 'farboo' }])); { - const data = await service.resolveVariables('Hello @foo and@far', CancellationToken.None); + const data = await service.resolveVariables('Hello @foo and@far', null!, CancellationToken.None); assert.strictEqual(Object.keys(data).length, 1); assert.deepEqual(Object.keys(data).sort(), ['foo']); } { - const data = await service.resolveVariables('@foo Hello', CancellationToken.None); + const data = await service.resolveVariables('@foo Hello', null!, CancellationToken.None); assert.strictEqual(Object.keys(data).length, 1); assert.deepEqual(Object.keys(data).sort(), ['foo']); } { - const data = await service.resolveVariables('Hello @foo', CancellationToken.None); + const data = await service.resolveVariables('Hello @foo', null!, CancellationToken.None); assert.strictEqual(Object.keys(data).length, 1); assert.deepEqual(Object.keys(data).sort(), ['foo']); } { - const data = await service.resolveVariables('Hello @foo and@far @foo', CancellationToken.None); + const data = await service.resolveVariables('Hello @foo and@far @foo', null!, CancellationToken.None); assert.strictEqual(Object.keys(data).length, 1); assert.deepEqual(Object.keys(data).sort(), ['foo']); } { - const data = await service.resolveVariables('Hello @foo and @far @foo', CancellationToken.None); + const data = await service.resolveVariables('Hello @foo and @far @foo', null!, CancellationToken.None); assert.strictEqual(Object.keys(data).length, 2); assert.deepEqual(Object.keys(data).sort(), ['far', 'foo']); } { - const data = await service.resolveVariables('Hello @foo and @far @foo @unknown', CancellationToken.None); + const data = await service.resolveVariables('Hello @foo and @far @foo @unknown', null!, CancellationToken.None); assert.strictEqual(Object.keys(data).length, 2); assert.deepEqual(Object.keys(data).sort(), ['far', 'foo']); } From d3fb23489deb3b9f33306e37e41365a3564e53c3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 26 Aug 2023 06:18:03 -0700 Subject: [PATCH 238/607] Make term decoration hover background opaque Fixes #191332 --- src/vs/workbench/contrib/terminal/browser/media/terminal.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index ca3f5ed9a05..c32f1c421b0 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -469,6 +469,8 @@ .monaco-workbench .terminal .terminal-command-decoration:not(.default):hover { cursor: pointer; +} +.monaco-workbench .terminal .terminal-command-decoration:not(.default):hover::before { border-radius: 5px; background-color: var(--vscode-toolbar-hoverBackground); } From 877c51bd1d8fe29389b83c766900e80486243943 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Sun, 27 Aug 2023 10:10:04 -0700 Subject: [PATCH 239/607] Resize Quick Chat (#191348) * Resize Quick Chat This is the first cut of allowing you to resize the quick chat... offering two ways: 1. scroll down to grow 2. sash at the bottom Maybe we should disable 1 if 2 was used... but lets see how these play together. * dispose * clean up variables * use a mutable disposable for the scheduling of the next animation frame --- .../contrib/chat/browser/chatQuick.ts | 16 ++++++++++-- .../contrib/chat/browser/chatWidget.ts | 26 ++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 6c3b84bab50..02a71564142 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; +import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -114,6 +115,7 @@ export class QuickChatService extends Disposable implements IQuickChatService { class QuickChat extends Disposable { private widget!: ChatWidget; + private sash!: Sash; private model: ChatModel | undefined; private _currentQuery: string | undefined; @@ -173,14 +175,24 @@ class QuickChat extends Disposable { }); } - this.registerListeners(); + this.sash?.dispose(); + this.sash = this._register(new Sash(parent, { getHorizontalSashTop: () => parent.offsetHeight }, { orientation: Orientation.HORIZONTAL })); + this.registerListeners(parent); } - private registerListeners(): void { + private registerListeners(parent: HTMLElement): void { this._register(this.widget.inputEditor.onDidChangeModelContent((e) => { this._currentQuery = this.widget.inputEditor.getValue(); })); this._register(this.widget.onDidClear(() => this.clear())); + this._register(this.sash.onDidChange((e) => { + if (e.currentY < 200) { + return; + } + this.widget.layout(e.currentY, parent.offsetWidth); + this.sash.layout(); + })); + this._register(this.widget.onDidChangeHeight((e) => this.sash.layout())); } async acceptInput(): Promise { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 7d61828c403..1a2e1758fbf 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -8,7 +8,7 @@ import { ITreeContextMenuEvent, ITreeElement } from 'vs/base/browser/ui/tree/tre import { disposableTimeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/chat'; @@ -64,6 +64,9 @@ export class ChatWidget extends Disposable implements IChatWidget { private _onDidAcceptInput = this._register(new Emitter()); readonly onDidAcceptInput = this._onDidAcceptInput.event; + private _onDidChangeHeight = this._register(new Emitter()); + readonly onDidChangeHeight = this._onDidChangeHeight.event; + private tree!: WorkbenchObjectTree; private renderer!: ChatListItemRenderer; @@ -518,6 +521,8 @@ export class ChatWidget extends Disposable implements IChatWidget { } this.listContainer.style.height = `${height - inputPartHeight}px`; + + this._onDidChangeHeight.fire(height); } private _dynamicMessageLayoutData?: { numOfMessages: number; maxHeight: number }; @@ -528,6 +533,25 @@ export class ChatWidget extends Disposable implements IChatWidget { setDynamicChatTreeItemLayout(numOfChatTreeItems: number, maxHeight: number) { this._dynamicMessageLayoutData = { numOfMessages: numOfChatTreeItems, maxHeight }; this._register(this.renderer.onDidChangeItemHeight(() => this.layoutDynamicChatTreeItemMode())); + + const mutableDisposable = this._register(new MutableDisposable()); + this._register(this.tree.onDidScroll((e) => { + mutableDisposable.value = dom.scheduleAtNextAnimationFrame(() => { + mutableDisposable.dispose(); + if (!e.scrollTopChanged || e.heightChanged || e.scrollHeightChanged) { + return; + } + const renderHeight = e.height; + const diff = e.scrollHeight - renderHeight - e.scrollTop; + if (diff === 0) { + return; + } + + const newHeight = Math.min(renderHeight + diff, maxHeight); + const inputPartHeight = this.inputPart.layout(newHeight, this.container.offsetWidth); + this.layout(newHeight + inputPartHeight, this.container.offsetWidth); + }); + })); } layoutDynamicChatTreeItemMode(): void { From 9b10df4bbfc46e2fd9be51f1f7eed698c28d10b0 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 26 Aug 2023 16:48:12 +0200 Subject: [PATCH 240/607] Includes collapseUnchangedRegions in diffEditor.editorVisibleTime telemetry event --- .../browser/widget/diffEditorWidget2/diffEditorWidget2.ts | 2 ++ src/vs/workbench/browser/parts/editor/textDiffEditor.ts | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index b8c42ae717f..03aee465520 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -81,6 +81,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly movedBlocksLinesPart = observableValue('MovedBlocksLinesPart', undefined); + public get collapseUnchangedRegions() { return this._options.collapseUnchangedRegions.get(); } + constructor( private readonly _domElement: HTMLElement, options: Readonly, diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 091111019e2..adb0fcddd35 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -332,17 +332,24 @@ export class TextDiffEditor extends AbstractTextEditor imp } private logInputLifecycleTelemetry(duration: number, languageId: string | undefined): void { + let collapseUnchangedRegions = false; + if (this.diffEditorControl instanceof DiffEditorWidget2) { + collapseUnchangedRegions = this.diffEditorControl.collapseUnchangedRegions; + } this.telemetryService.publicLog2<{ editorVisibleTimeMs: number; languageId: string; + collapseUnchangedRegions: boolean; }, { owner: 'hediet'; editorVisibleTimeMs: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Indicates the time the diff editor was visible to the user' }; languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Indicates for which language the diff editor was shown' }; + collapseUnchangedRegions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Indicates whether unchanged regions were collapsed' }; comment: 'This event gives insight about how long the diff editor was visible to the user.'; }>('diffEditor.editorVisibleTime', { editorVisibleTimeMs: duration, languageId: languageId ?? '', + collapseUnchangedRegions, }); } From b8914bdcdc053ec584fcdba6ecbbb170caf026b4 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 26 Aug 2023 18:47:28 +0200 Subject: [PATCH 241/607] Fixes dragging bug --- .../widget/diffEditorWidget2/unchangedRanges.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index b3ac905331d..c9a0352f5ed 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -94,13 +94,13 @@ export class UnchangedRangesFeature extends Disposable { const d = derived(reader => /** @description hiddenOriginalRangeStart */ r.getHiddenOriginalRange(reader).startLineNumber - 1); const origVz = new PlaceholderViewZone(d, 24); origViewZones.push(origVz); - store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, !sideBySide, modifiedOutlineSource)); + store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource)); } { const d = derived(reader => /** @description hiddenModifiedRangeStart */ r.getHiddenModifiedRange(reader).startLineNumber - 1); const modViewZone = new PlaceholderViewZone(d, 24); modViewZones.push(modViewZone); - store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, false, modifiedOutlineSource)); + store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, r.modifiedRange, false, modifiedOutlineSource)); } } @@ -262,6 +262,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { private readonly _editor: ICodeEditor, _viewZone: PlaceholderViewZone, private readonly _unchangedRegion: UnchangedRegion, + private readonly _unchangedRegionRange: LineRange, private readonly hide: boolean, private readonly _modifiedOutlineSource: OutlineSource, ) { @@ -333,9 +334,9 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { didMove = didMove || Math.abs(delta) > 2; const lineDelta = Math.round(delta / editor.getOption(EditorOption.lineHeight)); const newVal = Math.max(0, Math.min(cur - lineDelta, this._unchangedRegion.getMaxVisibleLineCountBottom())); - const top = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive); + const top = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); this._unchangedRegion.visibleLineCountBottom.set(newVal, undefined); - const top2 = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive); + const top2 = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); editor.setScrollTop(editor.getScrollTop() + (top2 - top)); }); @@ -343,10 +344,10 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { this._unchangedRegion.isDragged.set(false, undefined); if (!didMove) { - const top = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive); + const top = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); this._unchangedRegion.showMoreBelow(20, undefined); - const top2 = editor.getTopForLineNumber(this._unchangedRegion.originalRange.endLineNumberExclusive); + const top2 = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); editor.setScrollTop(editor.getScrollTop() + (top2 - top)); } this._nodes.bottom.classList.toggle('dragging', false); From b6326078fda6252a8c6f301bb05d59933e0c1e62 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 26 Aug 2023 23:17:36 +0200 Subject: [PATCH 242/607] Adds support for dryRun flag in diffEditor.switchSide --- .../diffEditorWidget2.contribution.ts | 9 +++++++-- .../widget/diffEditorWidget2/diffEditorWidget2.ts | 15 ++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts index 7421abcd386..6d538fe632d 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts @@ -120,11 +120,16 @@ export class SwitchSide extends EditorAction2 { }); } - runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { + runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, arg?: { dryRun: boolean }): unknown { const diffEditor = findFocusedDiffEditor(accessor); if (diffEditor instanceof DiffEditorWidget2) { - diffEditor.switchSide(); + if (arg && arg.dryRun) { + return { destinationSelection: diffEditor.mapToOtherSide().destinationSelection }; + } else { + diffEditor.switchSide(); + } } + return undefined; } } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 03aee465520..575f92e5f6b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -500,22 +500,31 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { await diffModel.waitForDiff(); } - switchSide(): void { + mapToOtherSide(): { destination: CodeEditorWidget; destinationSelection: Range | undefined } { const isModifiedFocus = this._editors.modified.hasWidgetFocus(); const source = isModifiedFocus ? this._editors.modified : this._editors.original; const destination = isModifiedFocus ? this._editors.original : this._editors.modified; + let destinationSelection: Range | undefined; + const sourceSelection = source.getSelection(); if (sourceSelection) { const mappings = this._diffModel.get()?.diff.get()?.mappings.map(m => isModifiedFocus ? m.lineRangeMapping.flip() : m.lineRangeMapping); if (mappings) { const newRange1 = translatePosition(sourceSelection.getStartPosition(), mappings); const newRange2 = translatePosition(sourceSelection.getEndPosition(), mappings); - const range = Range.plusRange(newRange1, newRange2); - destination.setSelection(range); + destinationSelection = Range.plusRange(newRange1, newRange2); } } + return { destination, destinationSelection }; + } + + switchSide(): void { + const { destination, destinationSelection } = this.mapToOtherSide(); destination.focus(); + if (destinationSelection) { + destination.setSelection(destinationSelection); + } } exitCompareMove(): void { From d6c6a048f7f3f0f425b7cf5d8a2d86081f2e8ee2 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 26 Aug 2023 23:32:02 +0200 Subject: [PATCH 243/607] Fixes monaco editor diff editor --- .../browser/widget/diffEditorWidget2/diffEditorEditors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index 877f79ce430..642359aeae4 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -99,7 +99,7 @@ export class DiffEditorEditors extends Disposable { // Disable unicode highlighting for the original side in inline mode, as they are not shown anyway. result.unicodeHighlight = { nonBasicASCII: false, ambiguousCharacters: false, invisibleCharacters: false }; } else { - result.unicodeHighlight = this._options.editorOptions.get().unicodeHighlight; + result.unicodeHighlight = this._options.editorOptions.get().unicodeHighlight || {}; result.wordWrapOverride1 = this._options.diffWordWrap.get(); } if (changedOptions.originalAriaLabel) { From b834c3c9d6a145f83390920d5681bef51e64aa38 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 26 Aug 2023 23:44:12 +0200 Subject: [PATCH 244/607] Fixes some smaller diff editor bugs --- src/vs/editor/browser/editorBrowser.ts | 2 +- .../diffEditorWidget2/diffEditorOptions.ts | 6 +--- .../diffEditorWidget2/movedBlocksLines.ts | 10 +++++-- .../diffEditorWidget2/unchangedRanges.ts | 28 +++++++++---------- src/vs/monaco.d.ts | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 1ea2dff1bb6..14ff56282d7 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -957,7 +957,7 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * Get the vertical position (top offset) for the line's top w.r.t. to the first line. */ - getTopForLineNumber(lineNumber: number): number; + getTopForLineNumber(lineNumber: number, includeViewZones?: boolean): number; /** * Get the vertical position (top offset) for the line's bottom w.r.t. to the first line. diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts index ee8aa4e9ca0..97a4e2adaaf 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts @@ -41,11 +41,7 @@ export class DiffEditorOptions { public readonly splitViewDefaultRatio = derived(reader => /** @description splitViewDefaultRatio */ this._options.read(reader).splitViewDefaultRatio); public readonly ignoreTrimWhitespace = derived(reader => /** @description ignoreTrimWhitespace */ this._options.read(reader).ignoreTrimWhitespace); public readonly maxComputationTimeMs = derived(reader => /** @description maxComputationTime */ this._options.read(reader).maxComputationTime); - public readonly showMoves = derived(reader => { - /** @description showMoves */ - const o = this._options.read(reader); - return o.experimental.showMoves! && o.renderSideBySide; - }); + public readonly showMoves = derived(reader => /** @description showMoves */ this._options.read(reader).experimental.showMoves! && this.renderSideBySide.read(reader)); public readonly isInEmbeddedEditor = derived(reader => /** @description isInEmbeddedEditor */ this._options.read(reader).isInEmbeddedEditor); public readonly diffWordWrap = derived(reader => /** @description diffWordWrap */ this._options.read(reader).diffWordWrap); public readonly originalEditable = derived(reader => /** @description originalEditable */ this._options.read(reader).originalEditable); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index c4144924bce..27a9f8f46fb 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -102,6 +102,9 @@ export class MovedBlocksLinesPart extends Disposable { })); } + private readonly _modifiedViewZonesChangedSignal = observableSignalFromEvent('modified.onDidChangeViewZones', this._editors.modified.onDidChangeViewZones); + private readonly _originalViewZonesChangedSignal = observableSignalFromEvent('original.onDidChangeViewZones', this._editors.original.onDidChangeViewZones); + private readonly _state = derivedWithStore('state', (reader, store) => { /** @description update moved blocks lines */ @@ -122,10 +125,13 @@ export class MovedBlocksLinesPart extends Disposable { return; } + this._modifiedViewZonesChangedSignal.read(reader); + this._originalViewZonesChangedSignal.read(reader); + const lines = moves.map((move) => { function computeLineStart(range: LineRange, editor: ICodeEditor) { - const t1 = editor.getTopForLineNumber(range.startLineNumber); - const t2 = editor.getTopForLineNumber(range.endLineNumberExclusive); + const t1 = editor.getTopForLineNumber(range.startLineNumber, true); + const t2 = editor.getTopForLineNumber(range.endLineNumberExclusive, true); return (t1 + t2) / 2; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index c9a0352f5ed..520a673300b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -361,27 +361,27 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { /** @description update labels */ const children: HTMLElement[] = []; - if (!this.hide && true) { + if (!this.hide) { const lineCount = _unchangedRegion.getHiddenModifiedRange(reader).length; const linesHiddenText = localize('hiddenLines', '{0} Hidden Lines', lineCount); children.push($('span', { title: linesHiddenText }, linesHiddenText)); - } - const range = this._unchangedRegion.getHiddenModifiedRange(reader); - const items = this._modifiedOutlineSource.getBreadcrumbItems(range, reader); + const range = this._unchangedRegion.getHiddenModifiedRange(reader); + const items = this._modifiedOutlineSource.getBreadcrumbItems(range, reader); - if (items.length > 0) { - children.push($('span', undefined, '\u00a0|\u00a0')); + if (items.length > 0) { + children.push($('span', undefined, '\u00a0|\u00a0')); - let isFirst = true; - for (const item of items) { - if (!isFirst) { - children.push($('span', {}, ' ', renderIcon(Codicon.chevronRight), ' ')); + let isFirst = true; + for (const item of items) { + if (!isFirst) { + children.push($('span', {}, ' ', renderIcon(Codicon.chevronRight), ' ')); + } + + const icon = SymbolKinds.toIcon(item.kind); + children.push($('span', {}, renderIcon(icon), ' ', item.name)); + isFirst = false; } - - const icon = SymbolKinds.toIcon(item.kind); - children.push($('span', {}, renderIcon(icon), ' ', item.name)); - isFirst = false; } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 2b0ba7176d8..442b460ca18 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5998,7 +5998,7 @@ declare namespace monaco.editor { /** * Get the vertical position (top offset) for the line's top w.r.t. to the first line. */ - getTopForLineNumber(lineNumber: number): number; + getTopForLineNumber(lineNumber: number, includeViewZones?: boolean): number; /** * Get the vertical position (top offset) for the line's bottom w.r.t. to the first line. */ From ea0c706089cf487303e0273e563f2293b1974828 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 28 Aug 2023 01:28:30 +0200 Subject: [PATCH 245/607] macOS: Window menu does not show opened windows in some cases (fix #191288) (#191405) * macOS: Window menu does not show opened windows in some cases (fix #191288) * :lipstick: --- .../workbench/browser/parts/titlebar/windowTitle.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index 28ae24ba4b8..64588af027b 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -12,7 +12,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { EditorResourceAccessor, Verbosity, SideBySideEditor } from 'vs/workbench/common/editor'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { isWindows, isWeb } from 'vs/base/common/platform'; +import { isWindows, isWeb, isMacintosh } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { trim } from 'vs/base/common/strings'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -110,6 +110,16 @@ export class WindowTitle extends Disposable { if (!trim(nativeTitle)) { nativeTitle = this.productService.nameLong; } + if (!window.document.title && isMacintosh && nativeTitle === this.productService.nameLong) { + // TODO@electron macOS: if we set a window title for + // the first time and it matches the one we set in + // `windowImpl.ts` somehow the window does not appear + // in the "Windows" menu. As such, we set the title + // briefly to something different to ensure macOS + // recognizes we have a window. + // See: https://github.com/microsoft/vscode/issues/191288 + window.document.title = `${this.productService.nameLong} ${WindowTitle.TITLE_DIRTY}`; + } window.document.title = nativeTitle; this.title = title; this.onDidChangeEmitter.fire(); From 16dc13553cb02cc1f1dae862d1b4d44a09a9d3d3 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Sun, 27 Aug 2023 16:33:24 -0700 Subject: [PATCH 246/607] debug: fix bad state when reloading extension host debug window (#191420) Not 100% sure why this fixed it, but it fixes it. Seems to be timing-related and it doesn't happen for me on OSS, but patching this into Insiders made the problem go away... --- src/vs/workbench/contrib/debug/browser/debugSession.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 2e2b758fc43..d95dc1fbf6e 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -10,7 +10,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { canceled } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; import { normalizeDriveLetter } from 'vs/base/common/labels'; -import { DisposableStore, dispose, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { mixin } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import * as resources from 'vs/base/common/resources'; @@ -29,7 +29,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession'; -import { AdapterEndEvent, IBreakpoint, IConfig, IDataBreakpoint, IDebugConfiguration, IDebugger, IDebugService, IDebugSession, IDebugSessionOptions, IExceptionBreakpoint, IExceptionInfo, IFunctionBreakpoint, IInstructionBreakpoint, IMemoryRegion, IRawModelUpdate, IRawStoppedDetails, IReplElement, IStackFrame, IThread, LoadedSourceEvent, State, VIEWLET_ID } from 'vs/workbench/contrib/debug/common/debug'; +import { AdapterEndEvent, IBreakpoint, IConfig, IDataBreakpoint, IDebugConfiguration, IDebugService, IDebugSession, IDebugSessionOptions, IDebugger, IExceptionBreakpoint, IExceptionInfo, IFunctionBreakpoint, IInstructionBreakpoint, IMemoryRegion, IRawModelUpdate, IRawStoppedDetails, IReplElement, IStackFrame, IThread, LoadedSourceEvent, State, VIEWLET_ID } from 'vs/workbench/contrib/debug/common/debug'; import { DebugCompoundRoot } from 'vs/workbench/contrib/debug/common/debugCompoundRoot'; import { DebugModel, ExpressionContainer, MemoryRegion, Thread } from 'vs/workbench/contrib/debug/common/debugModel'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; @@ -1262,7 +1262,7 @@ export class DebugSession implements IDebugSession, IDisposable { // Disconnects and clears state. Session can be initialized again for a new connection. private shutdown(): void { - dispose(this.rawListeners); + this.rawListeners.clear(); if (this.raw) { // Send out disconnect and immediatly dispose (do not wait for response) #127418 this.raw.disconnect({}); @@ -1279,7 +1279,7 @@ export class DebugSession implements IDebugSession, IDisposable { public dispose() { this.cancelAllRequests(); - dispose(this.rawListeners); + this.rawListeners.dispose(); } //---- sources From 56cdff656121a18c46dd6895b732751b721aaa8f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sun, 27 Aug 2023 23:02:29 +0200 Subject: [PATCH 247/607] Fixes hidden lines styling bug --- src/vs/editor/browser/widget/diffEditorWidget2/style.css | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/style.css b/src/vs/editor/browser/widget/diffEditorWidget2/style.css index aa681f8f617..80cd4921c25 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/style.css +++ b/src/vs/editor/browser/widget/diffEditorWidget2/style.css @@ -14,11 +14,15 @@ line-height: 14px; } -.monaco-editor .diff-hidden-lines:not(.dragging) .top:hover, .diff-hidden-lines:not(.dragging) .bottom:hover, .diff-hidden-lines .top.dragging, .diff-hidden-lines .bottom.dragging { +.monaco-editor .diff-hidden-lines:not(.dragging) .top:hover, +.monaco-editor .diff-hidden-lines:not(.dragging) .bottom:hover, +.monaco-editor .diff-hidden-lines .top.dragging, +.monaco-editor .diff-hidden-lines .bottom.dragging { background-color: var(--vscode-focusBorder); } -.monaco-editor .diff-hidden-lines .top, .diff-hidden-lines .bottom { +.monaco-editor .diff-hidden-lines .top, +.monaco-editor .diff-hidden-lines .bottom { transition: background-color 0.1s ease-out; height: 4px; background-color: transparent; From f7973f357e7c316a88e8817886f41a843021fe74 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sun, 27 Aug 2023 19:09:20 -0700 Subject: [PATCH 248/607] Change chat variable syntax to match remote agent proposal (#191427) This might be temporary, it's what we agreed to send around for now --- .../contrib/chat/common/chatServiceImpl.ts | 4 ++- .../contrib/chat/common/chatVariables.ts | 36 ++++++++++++++----- .../chat/test/common/chatVariables.test.ts | 33 ++++++++++------- 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index f3396c2053e..15977702756 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -508,7 +508,9 @@ export class ChatService extends Disposable implements IChatService { }; if (typeof request.message === 'string') { - request.variables = await this.chatVariablesService.resolveVariables(request.message, model, token); + const varResult = await this.chatVariablesService.resolveVariables(request.message, model, token); + request.variables = varResult.variables; + request.message = varResult.prompt; } rawResponse = await provider.provideReply(request, progressCallback, token); diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index 953888691ef..68bac81975f 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -38,7 +38,7 @@ export interface IChatVariablesService { /** * Resolves all variables that occur in `prompt` */ - resolveVariables(prompt: string, model: IChatModel, token: CancellationToken): Promise>; + resolveVariables(prompt: string, model: IChatModel, token: CancellationToken): Promise; } interface IChatData { @@ -46,6 +46,11 @@ interface IChatData { resolver: IChatVariableResolver; } +interface IChatVariableResolveResult { + variables: Record; + prompt: string; +} + export class ChatVariablesService implements IChatVariablesService { declare _serviceBrand: undefined; @@ -54,32 +59,47 @@ export class ChatVariablesService implements IChatVariablesService { constructor() { } - async resolveVariables(prompt: string, model: IChatModel, token: CancellationToken): Promise> { + async resolveVariables(prompt: string, model: IChatModel, token: CancellationToken): Promise { const resolvedVariables: Record = {}; const jobs: Promise[] = []; - const regex = /(^|\s)@(\w+)(:\w+)?(\s|$)/ig; + // TODO have a separate parser that is also used for decorations + const regex = /(^|\s)@(\w+)(:\w+)?(?=\s|$|\b)/ig; + let lastMatch = 0; + const parsedPrompt: string[] = []; let match: RegExpMatchArray | null; while (match = regex.exec(prompt)) { - const candidate = match[2]; - const data = this._resolver.get(candidate.toLowerCase()); + const [fullMatch, leading, varName, arg] = match; + const data = this._resolver.get(varName.toLowerCase()); if (data) { - const arg = match[3]; if (!arg || data.data.canTakeArgument) { + parsedPrompt.push(prompt.substring(lastMatch, match.index!)); + parsedPrompt.push(''); + lastMatch = match.index! + fullMatch.length; + const varIndex = parsedPrompt.length - 1; const argWithoutColon = arg?.slice(1); + const fullVarName = varName + (arg ?? ''); jobs.push(data.resolver(prompt, argWithoutColon, model, token).then(value => { if (value) { - resolvedVariables[candidate + (arg ?? '')] = value; + resolvedVariables[fullVarName] = value; + parsedPrompt[varIndex] = `${leading}[@${fullVarName}](values:${fullVarName})`; + } else { + parsedPrompt[varIndex] = fullMatch; } }).catch(onUnexpectedExternalError)); } } } + parsedPrompt.push(prompt.substring(lastMatch)); + await Promise.allSettled(jobs); - return Promise.resolve(resolvedVariables); + return { + variables: resolvedVariables, + prompt: parsedPrompt.join('') + }; } getVariables(): Iterable> { diff --git a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts index ac6644efe83..db9c3f69a74 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts @@ -22,33 +22,42 @@ suite('ChatVariables', function () { { const data = await service.resolveVariables('Hello @foo and@far', null!, CancellationToken.None); - assert.strictEqual(Object.keys(data).length, 1); - assert.deepEqual(Object.keys(data).sort(), ['foo']); + assert.strictEqual(Object.keys(data.variables).length, 1); + assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); + assert.strictEqual(data.prompt, 'Hello [@foo](values:foo) and@far'); } { const data = await service.resolveVariables('@foo Hello', null!, CancellationToken.None); - assert.strictEqual(Object.keys(data).length, 1); - assert.deepEqual(Object.keys(data).sort(), ['foo']); + assert.strictEqual(Object.keys(data.variables).length, 1); + assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); + assert.strictEqual(data.prompt, '[@foo](values:foo) Hello'); } { const data = await service.resolveVariables('Hello @foo', null!, CancellationToken.None); - assert.strictEqual(Object.keys(data).length, 1); - assert.deepEqual(Object.keys(data).sort(), ['foo']); + assert.strictEqual(Object.keys(data.variables).length, 1); + assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); + } + { + const data = await service.resolveVariables('Hello @foo?', null!, CancellationToken.None); + assert.strictEqual(Object.keys(data.variables).length, 1); + assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); + assert.strictEqual(data.prompt, 'Hello [@foo](values:foo)?'); } { const data = await service.resolveVariables('Hello @foo and@far @foo', null!, CancellationToken.None); - assert.strictEqual(Object.keys(data).length, 1); - assert.deepEqual(Object.keys(data).sort(), ['foo']); + assert.strictEqual(Object.keys(data.variables).length, 1); + assert.deepEqual(Object.keys(data.variables).sort(), ['foo']); } { const data = await service.resolveVariables('Hello @foo and @far @foo', null!, CancellationToken.None); - assert.strictEqual(Object.keys(data).length, 2); - assert.deepEqual(Object.keys(data).sort(), ['far', 'foo']); + assert.strictEqual(Object.keys(data.variables).length, 2); + assert.deepEqual(Object.keys(data.variables).sort(), ['far', 'foo']); } { const data = await service.resolveVariables('Hello @foo and @far @foo @unknown', null!, CancellationToken.None); - assert.strictEqual(Object.keys(data).length, 2); - assert.deepEqual(Object.keys(data).sort(), ['far', 'foo']); + assert.strictEqual(Object.keys(data.variables).length, 2); + assert.deepEqual(Object.keys(data.variables).sort(), ['far', 'foo']); + assert.strictEqual(data.prompt, 'Hello [@foo](values:foo) and [@far](values:far) [@foo](values:foo) @unknown'); } }); }); From d87941aa3222d27f3f08da31aed9a013b3920a83 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 28 Aug 2023 00:10:25 -0700 Subject: [PATCH 249/607] Create only one widget for quick chat (#191431) We were creating a widget per-showing of the quick pick... but that isn't efficient. This PR creates a single widget and maintains that for the life of the window. From @Tyriar in https://github.com/microsoft/vscode/pull/191348#discussion_r1306440725 --- .../contrib/chat/browser/chatQuick.ts | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 02a71564142..32b6ca797fd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -8,7 +8,7 @@ import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IContextKeyService, IScopedContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput'; @@ -26,7 +26,9 @@ export class QuickChatService extends Disposable implements IQuickChatService { readonly onDidClose = this._onDidClose.event; private _input: IQuickWidget | undefined; + // TODO: support multiple chat providers eventually private _currentChat: QuickChat | undefined; + private _container: HTMLElement | undefined; constructor( @IQuickInputService private readonly quickInputService: IQuickInputService, @@ -77,16 +79,18 @@ export class QuickChatService extends Disposable implements IQuickChatService { this._input.ignoreFocusOut = true; disposableStore.add(this._input); - const containerSession = dom.$('.interactive-session'); - this._input.widget = containerSession; + this._container ??= dom.$('.interactive-session'); + this._input.widget = this._container; - this._currentChat ??= this.instantiationService.createInstance(QuickChat, { - providerId: providerInfo.id, - }); - - // show needs to come before the current chat rendering this._input.show(); - this._currentChat.render(containerSession); + if (!this._currentChat) { + this._currentChat = this.instantiationService.createInstance(QuickChat, { + providerId: providerInfo.id, + }); + + // show needs to come after the quickpick is shown + this._currentChat.render(this._container); + } disposableStore.add(this._input.onDidHide(() => { disposableStore.dispose(); @@ -106,6 +110,7 @@ export class QuickChatService extends Disposable implements IQuickChatService { } close(): void { this._input?.dispose(); + this._input = undefined; } async openInChatView(): Promise { await this._currentChat?.openChatView(); @@ -119,11 +124,6 @@ class QuickChat extends Disposable { private model: ChatModel | undefined; private _currentQuery: string | undefined; - private _scopedContextKeyService!: IScopedContextKeyService; - get scopedContextKeyService() { - return this._scopedContextKeyService; - } - constructor( private readonly _options: IChatViewOptions, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -144,14 +144,28 @@ class QuickChat extends Disposable { focus(): void { if (this.widget) { this.widget.focusInput(); + const value = this.widget.inputEditor.getValue(); + if (value) { + this.widget.inputEditor.setSelection({ + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: value.length + 1 + }); + } } } render(parent: HTMLElement): void { - this._scopedContextKeyService?.dispose(); - this._scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent)); - const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); - this.widget?.dispose(); + if (this.widget) { + throw new Error('Cannot render quick chat twice'); + } + const scopedInstantiationService = this.instantiationService.createChild( + new ServiceCollection([ + IContextKeyService, + this._register(this.contextKeyService.createScoped(parent)) + ]) + ); this.widget = this._register( scopedInstantiationService.createInstance( ChatWidget, @@ -166,16 +180,6 @@ class QuickChat extends Disposable { this.widget.setVisible(true); this.widget.setDynamicChatTreeItemLayout(2, 900); this.updateModel(); - if (this._currentQuery) { - this.widget.inputEditor.setSelection({ - startLineNumber: 1, - startColumn: 1, - endLineNumber: 1, - endColumn: this._currentQuery.length + 1 - }); - } - - this.sash?.dispose(); this.sash = this._register(new Sash(parent, { getHorizontalSashTop: () => parent.offsetHeight }, { orientation: Orientation.HORIZONTAL })); this.registerListeners(parent); } @@ -185,14 +189,15 @@ class QuickChat extends Disposable { this._currentQuery = this.widget.inputEditor.getValue(); })); this._register(this.widget.onDidClear(() => this.clear())); + this._register(this.widget.onDidChangeHeight((e) => this.sash.layout())); + const width = parent.offsetWidth; this._register(this.sash.onDidChange((e) => { if (e.currentY < 200) { return; } - this.widget.layout(e.currentY, parent.offsetWidth); + this.widget.layout(e.currentY, width); this.sash.layout(); })); - this._register(this.widget.onDidChangeHeight((e) => this.sash.layout())); } async acceptInput(): Promise { @@ -228,6 +233,7 @@ class QuickChat extends Disposable { setValue(value: string): void { this.widget.inputEditor.setValue(value); + this.focus(); } private updateModel(): void { From 1a99420d57e7fad26571d7cd6ebe3de77955f533 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 28 Aug 2023 00:10:51 -0700 Subject: [PATCH 250/607] Misc PR feedback for chatWidget (#191432) * Use cached width first to prevent trip to dom * remove irrelevant dispose call * fix CSS of inputbox to align it to Quick Pick --- src/vs/workbench/contrib/chat/browser/chatWidget.ts | 6 +++--- src/vs/workbench/contrib/chat/browser/media/chat.css | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 1a2e1758fbf..500ff2b372b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -537,7 +537,6 @@ export class ChatWidget extends Disposable implements IChatWidget { const mutableDisposable = this._register(new MutableDisposable()); this._register(this.tree.onDidScroll((e) => { mutableDisposable.value = dom.scheduleAtNextAnimationFrame(() => { - mutableDisposable.dispose(); if (!e.scrollTopChanged || e.heightChanged || e.scrollHeightChanged) { return; } @@ -548,8 +547,9 @@ export class ChatWidget extends Disposable implements IChatWidget { } const newHeight = Math.min(renderHeight + diff, maxHeight); - const inputPartHeight = this.inputPart.layout(newHeight, this.container.offsetWidth); - this.layout(newHeight + inputPartHeight, this.container.offsetWidth); + const width = this.bodyDimension?.width ?? this.container.offsetWidth; + const inputPartHeight = this.inputPart.layout(newHeight, width); + this.layout(newHeight + inputPartHeight, width); }); })); } diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 33b798459ea..60b78970f75 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -426,6 +426,7 @@ .quick-input-widget .interactive-session .interactive-input-and-execute-toolbar { margin: 0; + border-radius: 2px; } /* #endregion */ From 45e2e0bfd066a19c996ba6091811c254224c60fb Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 28 Aug 2023 00:49:40 -0700 Subject: [PATCH 251/607] JS/TS package acquisition (#184438) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Experiment with adding ata using `@types` packages shipped in an extension * Use own file system instead of `https` * JS/TS type support on web * Tsconfig needs esModuleInterop not module:nodenext We actually want webpack to emit commonjs, but need to write ES default imports to use node-maintainer * fix package.json indentation * Adding setting to disable web type acquisition * Fix merge of yarn lock * Fixing merge errors * Fixing errors * Pick up package externally * Fixing conflicts * Bump version --------- Co-authored-by: Kat Marchán Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> --- .eslintignore | 1 + build/filters.js | 7 +- extensions/shared.webpack.config.js | 3 + .../typescript-language-features/package.json | 14 +- .../package.nls.json | 17 +- .../src/configuration/configuration.ts | 6 + .../src/extension.browser.ts | 20 +- .../src/filesystems/autoInstallerFs.ts | 252 +++++++++++++++++ .../src/filesystems/memFs.ts | 198 +++++++++++++ .../src/tsServer/serverProcess.browser.ts | 12 +- .../tsconfig.json | 1 + .../web/README.md | 177 +++--------- .../web/jsTyping.ts | 78 ++++++ .../web/tsconfig.json | 3 +- .../web/typingsInstaller.ts | 213 ++++++++++++++ .../web/webServer.ts | 263 +++++++++++++----- .../typescript-language-features/yarn.lock | 63 ++--- 17 files changed, 1057 insertions(+), 271 deletions(-) create mode 100644 extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts create mode 100644 extensions/typescript-language-features/src/filesystems/memFs.ts create mode 100644 extensions/typescript-language-features/web/jsTyping.ts create mode 100644 extensions/typescript-language-features/web/typingsInstaller.ts diff --git a/.eslintignore b/.eslintignore index fea65b49587..7bbd3778e90 100644 --- a/.eslintignore +++ b/.eslintignore @@ -15,6 +15,7 @@ **/extensions/typescript-language-features/test-workspace/** **/extensions/typescript-language-features/extension.webpack.config.js **/extensions/typescript-language-features/extension-browser.webpack.config.js +**/extensions/typescript-language-features/package-manager/node-maintainer/** **/extensions/vscode-api-tests/testWorkspace/** **/extensions/vscode-api-tests/testWorkspace2/** **/fixtures/** diff --git a/build/filters.js b/build/filters.js index 5a8cf36ef84..0e0c5fcabb8 100644 --- a/build/filters.js +++ b/build/filters.js @@ -37,7 +37,7 @@ module.exports.unicodeFilter = [ '!LICENSES.chromium.html', '!**/LICENSE', - '!**/*.{dll,exe,png,bmp,jpg,scpt,cur,ttf,woff,eot,template,ico,icns,opus}', + '!**/*.{dll,exe,png,bmp,jpg,scpt,cur,ttf,woff,eot,template,ico,icns,opus,wasm}', '!**/test/**', '!**/*.test.ts', '!**/*.{d.ts,json,md}', @@ -88,6 +88,7 @@ module.exports.indentationFilter = [ '!test/smoke/out/**', '!extensions/typescript-language-features/test-workspace/**', '!extensions/typescript-language-features/resources/walkthroughs/**', + '!extensions/typescript-language-features/package-manager/node-maintainer/**', '!extensions/markdown-math/notebook-out/**', '!extensions/ipynb/notebook-out/**', '!extensions/vscode-api-tests/testWorkspace/**', @@ -115,7 +116,7 @@ module.exports.indentationFilter = [ '!src/vs/*/**/*.d.ts', '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', - '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist,opus,admx,adml}', + '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist,opus,admx,adml,wasm}', '!build/{lib,download,linux,darwin}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', @@ -157,6 +158,7 @@ module.exports.copyrightFilter = [ '!**/*.disabled', '!**/*.code-workspace', '!**/*.js.map', + '!**/*.wasm', '!build/**/*.init', '!build/linux/libcxx-fetcher.*', '!resources/linux/snap/snapcraft.yaml', @@ -166,6 +168,7 @@ module.exports.copyrightFilter = [ '!extensions/markdown-language-features/media/highlight.css', '!extensions/markdown-math/notebook-out/**', '!extensions/ipynb/notebook-out/**', + '!extensions/typescript-language-features/node-maintainer/**', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*', '!src/vs/editor/test/node/classification/typescript-test.ts', diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index cd9aef49677..19e76a9d35f 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -139,6 +139,9 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo }, }, ] + }, { + test: /\.wasm$/, + type: 'asset/inline' }] }, externals: { diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index e3c9a7f870d..38730118883 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -34,12 +34,13 @@ ], "dependencies": { "@vscode/extension-telemetry": "^0.8.4", - "jsonc-parser": "^3.2.0", - "semver": "7.5.2", - "vscode-tas-client": "^0.1.63", "@vscode/sync-api-client": "^0.7.2", "@vscode/sync-api-common": "^0.7.2", "@vscode/sync-api-service": "^0.7.3", + "@vscode/ts-package-manager": "^0.0.2", + "jsonc-parser": "^3.2.0", + "semver": "7.5.2", + "vscode-tas-client": "^0.1.63", "vscode-uri": "^3.0.3" }, "devDependencies": { @@ -1250,6 +1251,13 @@ "description": "%configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors%", "scope": "window" }, + "typescript.experimental.tsserver.web.typeAcquisition.enabled": { + "type": "boolean", + "default": false, + "description": "%configuration.experimental.tsserver.web.typeAcquisition.enabled%", + "scope": "window", + "tags": ["experimental"] + }, "typescript.preferGoToSourceDefinition": { "type": "boolean", "default": false, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 81260c405e1..641a4092870 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -212,5 +212,20 @@ "configuration.suggest.classMemberSnippets.enabled": "Enable/disable snippet completions for class members.", "configuration.suggest.objectLiteralMethodSnippets.enabled": "Enable/disable snippet completions for methods in object literals. Requires using TypeScript 4.7+ in the workspace.", "configuration.tsserver.web.projectWideIntellisense.enabled": "Enable/disable project-wide IntelliSense on web. Requires that VS Code is running in a trusted context.", - "configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors": "Suppresses semantic errors. This is needed when using external packages as these can't be included analyzed on web." + "configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors": "Suppresses semantic errors. This is needed when using external packages as these can't be included analyzed on web.", + "configuration.experimental.tsserver.web.typeAcquisition.enabled": "Enable/disable package acquisition on the web.", + "walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js", + "walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.", + "walkthroughs.nodejsWelcome.downloadNode.forMacOrWindows.title": "Install Node.js", + "walkthroughs.nodejsWelcome.downloadNode.forMacOrWindows.description": "Node.js is an easy way to run JavaScript code. You can use it to quickly build command-line apps and servers. It also comes with npm, a package manager which makes reusing and sharing JavaScript code easy.\n[Install Node.js](https://nodejs.org/en/download/)", + "walkthroughs.nodejsWelcome.downloadNode.forLinux.title": "Install Node.js", + "walkthroughs.nodejsWelcome.downloadNode.forLinux.description": "Node.js is an easy way to run JavaScript code. You can use it to quickly build command-line apps and servers. It also comes with npm, a package manager which makes reusing and sharing JavaScript code easy.\n[Install Node.js](https://nodejs.org/en/download/package-manager/)", + "walkthroughs.nodejsWelcome.makeJsFile.title": "Create a JavaScript File", + "walkthroughs.nodejsWelcome.makeJsFile.description": "Let's write our first JavaScript file. We'll have to create a new file and save it with the ``.js`` extension at the end of the file name.\n[Create a JavaScript File](command:javascript-walkthrough.commands.createJsFile)", + "walkthroughs.nodejsWelcome.debugJsFile.title": "Run and Debug your JavaScript", + "walkthroughs.nodejsWelcome.debugJsFile.description": "Once you've installed Node.js, you can run JavaScript programs at a terminal by entering ``node your-file-name.js``\nAnother easy way to run Node.js programs is by using VS Code's debugger which lets you run your code, pause at different points, and help you understand what's going on step-by-step.\n[Start Debugging](command:javascript-walkthrough.commands.debugJsFile)", + "walkthroughs.nodejsWelcome.debugJsFile.altText": "Debug and run your JavaScript code in Node.js with Visual Studio Code.", + "walkthroughs.nodejsWelcome.learnMoreAboutJs.title": "Explore More", + "walkthroughs.nodejsWelcome.learnMoreAboutJs.description": "Want to get more comfortable with JavaScript, Node.js, and VS Code? Be sure to check out our docs!\nWe've got lots of resources for learning [JavaScript](https://code.visualstudio.com/docs/nodejs/working-with-javascript) and [Node.js](https://code.visualstudio.com/docs/nodejs/nodejs-tutorial).\n\n[Learn More](https://code.visualstudio.com/docs/nodejs/nodejs-tutorial)", + "walkthroughs.nodejsWelcome.learnMoreAboutJs.altText": "Learn more about JavaScript and Node.js in Visual Studio Code." } diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index d338792c58c..f3a5817146b 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -112,6 +112,7 @@ export interface TypeScriptServiceConfiguration { readonly useSyntaxServer: SyntaxServerConfiguration; readonly webProjectWideIntellisenseEnabled: boolean; readonly webProjectWideIntellisenseSuppressSemanticErrors: boolean; + readonly webExperimentalTypeAcquisition: boolean; readonly enableDiagnosticsTelemetry: boolean; readonly enableProjectDiagnostics: boolean; readonly maxTsServerMemory: number; @@ -145,6 +146,7 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu useSyntaxServer: this.readUseSyntaxServer(configuration), webProjectWideIntellisenseEnabled: this.readWebProjectWideIntellisenseEnable(configuration), webProjectWideIntellisenseSuppressSemanticErrors: this.readWebProjectWideIntellisenseSuppressSemanticErrors(configuration), + webExperimentalTypeAcquisition: this.readWebExperimentalTypeAcquisition(configuration), enableDiagnosticsTelemetry: this.readEnableDiagnosticsTelemetry(configuration), enableProjectDiagnostics: this.readEnableProjectDiagnostics(configuration), maxTsServerMemory: this.readMaxTsServerMemory(configuration), @@ -175,6 +177,10 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu return configuration.get('typescript.disableAutomaticTypeAcquisition', false); } + protected readWebExperimentalTypeAcquisition(configuration: vscode.WorkspaceConfiguration): boolean { + return configuration.get('typescript.experimental.tsserver.web.typeAcquisition.enabled', false); + } + protected readLocale(configuration: vscode.WorkspaceConfiguration): string | null { const value = configuration.get('typescript.locale', 'auto'); return !value || value === 'auto' ? null : value; diff --git a/extensions/typescript-language-features/src/extension.browser.ts b/extensions/typescript-language-features/src/extension.browser.ts index 392a81f7922..65e9d57fc38 100644 --- a/extensions/typescript-language-features/src/extension.browser.ts +++ b/extensions/typescript-language-features/src/extension.browser.ts @@ -8,22 +8,24 @@ import * as vscode from 'vscode'; import { Api, getExtensionApi } from './api'; import { CommandManager } from './commands/commandManager'; import { registerBaseCommands } from './commands/index'; +import { TypeScriptServiceConfiguration } from './configuration/configuration'; +import { BrowserServiceConfigurationProvider } from './configuration/configuration.browser'; import { ExperimentationTelemetryReporter, IExperimentationTelemetryReporter } from './experimentTelemetryReporter'; +import { AutoInstallerFs } from './filesystems/autoInstallerFs'; +import { MemFs } from './filesystems/memFs'; import { createLazyClientHost, lazilyActivateClient } from './lazyClientHost'; +import { Logger } from './logging/logger'; import RemoteRepositories from './remoteRepositories.browser'; import { API } from './tsServer/api'; import { noopRequestCancellerFactory } from './tsServer/cancellation'; import { noopLogDirectoryProvider } from './tsServer/logDirectoryProvider'; +import { PluginManager } from './tsServer/plugins'; import { WorkerServerProcessFactory } from './tsServer/serverProcess.browser'; import { ITypeScriptVersionProvider, TypeScriptVersion, TypeScriptVersionSource } from './tsServer/versionProvider'; import { ActiveJsTsEditorTracker } from './ui/activeJsTsEditorTracker'; -import { TypeScriptServiceConfiguration } from './configuration/configuration'; -import { BrowserServiceConfigurationProvider } from './configuration/configuration.browser'; -import { Logger } from './logging/logger'; +import { Disposable } from './utils/dispose'; import { getPackageInfo } from './utils/packageInfo'; import { isWebAndHasSharedArrayBuffers } from './utils/platform'; -import { PluginManager } from './tsServer/plugins'; -import { Disposable } from './utils/dispose'; class StaticVersionProvider implements ITypeScriptVersionProvider { @@ -99,6 +101,14 @@ export async function activate(context: vscode.ExtensionContext): Promise { context.subscriptions.push(lazilyActivateClient(lazyClientHost, pluginManager, activeJsTsEditorTracker, async () => { await startPreloadWorkspaceContentsIfNeeded(context, logger); })); + context.subscriptions.push(vscode.workspace.registerFileSystemProvider('vscode-global-typings', new MemFs(), { + isCaseSensitive: true, + isReadonly: false + })); + context.subscriptions.push(vscode.workspace.registerFileSystemProvider('vscode-node-modules', new AutoInstallerFs(), { + isCaseSensitive: true, + isReadonly: false + })); return getExtensionApi(onCompletionAccepted.event, pluginManager); } diff --git a/extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts b/extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts new file mode 100644 index 00000000000..4e69fce8cda --- /dev/null +++ b/extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts @@ -0,0 +1,252 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { MemFs } from './memFs'; +import { URI } from 'vscode-uri'; +import { PackageManager, FileSystem, packagePath } from '@vscode/ts-package-manager'; +import { join, basename, dirname } from 'path'; + +const TEXT_DECODER = new TextDecoder('utf-8'); +const TEXT_ENCODER = new TextEncoder(); + +export class AutoInstallerFs implements vscode.FileSystemProvider { + + private readonly memfs = new MemFs(); + private readonly fs: FileSystem; + private readonly projectCache = new Map>(); + private readonly watcher: vscode.FileSystemWatcher; + private readonly _emitter = new vscode.EventEmitter(); + + readonly onDidChangeFile: vscode.Event = this._emitter.event; + + constructor() { + this.watcher = vscode.workspace.createFileSystemWatcher('**/{package.json,package-lock.json,package-lock.kdl}'); + const handler = (uri: URI) => { + const root = dirname(uri.path); + if (this.projectCache.delete(root)) { + (async () => { + const pm = new PackageManager(this.fs); + const opts = await this.getInstallOpts(uri, root); + const proj = await pm.resolveProject(root, opts); + proj.pruneExtraneous(); + // TODO: should this fire on vscode-node-modules instead? + // NB(kmarchan): This should tell TSServer that there's + // been changes inside node_modules and it needs to + // re-evaluate things. + this._emitter.fire([{ + type: vscode.FileChangeType.Changed, + uri: uri.with({ path: join(root, 'node_modules') }) + }]); + })(); + } + }; + this.watcher.onDidChange(handler); + this.watcher.onDidCreate(handler); + this.watcher.onDidDelete(handler); + const memfs = this.memfs; + memfs.onDidChangeFile((e) => { + this._emitter.fire(e.map(ev => ({ + type: ev.type, + // TODO: we're gonna need a MappedUri dance... + uri: ev.uri.with({ scheme: 'memfs' }) + }))); + }); + this.fs = { + readDirectory(path: string, _extensions?: readonly string[], _exclude?: readonly string[], _include?: readonly string[], _depth?: number): string[] { + return memfs.readDirectory(URI.file(path)).map(([name, _]) => name); + }, + + deleteFile(path: string): void { + memfs.delete(URI.file(path)); + }, + + createDirectory(path: string): void { + memfs.createDirectory(URI.file(path)); + }, + + writeFile(path: string, data: string, _writeByteOrderMark?: boolean): void { + memfs.writeFile(URI.file(path), TEXT_ENCODER.encode(data), { overwrite: true, create: true }); + }, + + directoryExists(path: string): boolean { + try { + const stat = memfs.stat(URI.file(path)); + return stat.type === vscode.FileType.Directory; + } catch (e) { + return false; + } + }, + + readFile(path: string, _encoding?: string): string | undefined { + try { + return TEXT_DECODER.decode(memfs.readFile(URI.file(path))); + } catch (e) { + return undefined; + } + } + }; + } + + watch(resource: vscode.Uri): vscode.Disposable { + const mapped = URI.file(new MappedUri(resource).path); + console.log('watching', mapped); + return this.memfs.watch(mapped); + } + + async stat(uri: vscode.Uri): Promise { + // console.log('stat', uri.toString()); + const mapped = new MappedUri(uri); + + // TODO: case sensitivity configuration + + // We pretend every single node_modules or @types directory ever actually + // exists. + if (basename(mapped.path) === 'node_modules' || basename(mapped.path) === '@types') { + return { + mtime: 0, + ctime: 0, + type: vscode.FileType.Directory, + size: 0 + }; + } + + await this.ensurePackageContents(mapped); + + return this.memfs.stat(URI.file(mapped.path)); + } + + async readDirectory(uri: vscode.Uri): Promise<[string, vscode.FileType][]> { + // console.log('readDirectory', uri.toString()); + const mapped = new MappedUri(uri); + await this.ensurePackageContents(mapped); + + return this.memfs.readDirectory(URI.file(mapped.path)); + } + + async readFile(uri: vscode.Uri): Promise { + // console.log('readFile', uri.toString()); + const mapped = new MappedUri(uri); + await this.ensurePackageContents(mapped); + + return this.memfs.readFile(URI.file(mapped.path)); + } + + writeFile(_uri: vscode.Uri, _content: Uint8Array, _options: { create: boolean; overwrite: boolean }): void { + throw new Error('not implemented'); + } + + rename(_oldUri: vscode.Uri, _newUri: vscode.Uri, _options: { overwrite: boolean }): void { + throw new Error('not implemented'); + } + + delete(_uri: vscode.Uri): void { + throw new Error('not implemented'); + } + + createDirectory(_uri: vscode.Uri): void { + throw new Error('not implemented'); + } + + private async ensurePackageContents(incomingUri: MappedUri): Promise { + // console.log('ensurePackageContents', incomingUri.path); + + // If we're not looking for something inside node_modules, bail early. + if (!incomingUri.path.includes('node_modules')) { + throw vscode.FileSystemError.FileNotFound(); + } + + // standard lib files aren't handled through here + if (incomingUri.path.includes('node_modules/@typescript') || incomingUri.path.includes('node_modules/@types/typescript__')) { + throw vscode.FileSystemError.FileNotFound(); + } + + const root = this.getProjectRoot(incomingUri.path); + + const pkgPath = packagePath(incomingUri.path); + if (!root || this.projectCache.get(root)?.has(pkgPath)) { + return; + } + + const proj = await (new PackageManager(this.fs)).resolveProject(root, await this.getInstallOpts(incomingUri.original, root)); + + const restore = proj.restorePackageAt(incomingUri.path); + try { + await restore; + } catch (e) { + console.error(`failed to restore package at ${incomingUri.path}: `, e); + throw e; + } + if (!this.projectCache.has(root)) { + this.projectCache.set(root, new Set()); + } + this.projectCache.get(root)!.add(pkgPath); + } + + private async getInstallOpts(originalUri: URI, root: string) { + const vsfs = vscode.workspace.fs; + let pkgJson; + try { + pkgJson = TEXT_DECODER.decode(await vsfs.readFile(originalUri.with({ path: join(root, 'package.json') }))); + } catch (e) { } + + let kdlLock; + try { + kdlLock = TEXT_DECODER.decode(await vsfs.readFile(originalUri.with({ path: join(root, 'package-lock.kdl') }))); + } catch (e) { } + + let npmLock; + try { + npmLock = TEXT_DECODER.decode(await vsfs.readFile(originalUri.with({ path: join(root, 'package-lock.json') }))); + } catch (e) { } + + return { + pkgJson, + kdlLock, + npmLock + }; + } + + private getProjectRoot(path: string): string | undefined { + const pkgPath = path.match(/(^.*)\/node_modules/); + return pkgPath?.[1]; + } + + // --- manage file events + +} + +class MappedUri { + readonly raw: vscode.Uri; + readonly original: vscode.Uri; + readonly mapped: vscode.Uri; + constructor(uri: vscode.Uri) { + this.raw = uri; + + const parts = uri.path.match(/^\/([^\/]+)\/([^\/]*)(?:\/(.+))?$/); + if (!parts) { + throw new Error(`Invalid path: ${uri.path}`); + } + + const scheme = parts[1]; + const authority = parts[2] === 'ts-nul-authority' ? '' : parts[2]; + const path = parts[3]; + this.original = URI.from({ scheme, authority, path: (path ? '/' + path : path) }); + this.mapped = this.original.with({ scheme: this.raw.scheme, authority: this.raw.authority }); + } + + get path() { + return this.mapped.path; + } + get scheme() { + return this.mapped.scheme; + } + get authority() { + return this.mapped.authority; + } + get flatPath() { + return join('/', this.scheme, this.authority, this.path); + } +} diff --git a/extensions/typescript-language-features/src/filesystems/memFs.ts b/extensions/typescript-language-features/src/filesystems/memFs.ts new file mode 100644 index 00000000000..02476ec1804 --- /dev/null +++ b/extensions/typescript-language-features/src/filesystems/memFs.ts @@ -0,0 +1,198 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { basename, dirname } from 'path'; + +export class MemFs implements vscode.FileSystemProvider { + + private readonly root = new FsEntry( + new Map(), + 0, + 0, + ); + + stat(uri: vscode.Uri): vscode.FileStat { + // console.log('stat', uri.toString()); + const entry = this.getEntry(uri); + if (!entry) { + throw vscode.FileSystemError.FileNotFound(); + } + + return entry; + } + + readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { + // console.log('readDirectory', uri.toString()); + + const entry = this.getEntry(uri); + if (!entry) { + throw vscode.FileSystemError.FileNotFound(); + } + + return [...entry.contents.entries()].map(([name, entry]) => [name, entry.type]); + } + + readFile(uri: vscode.Uri): Uint8Array { + // console.log('readFile', uri.toString()); + + const entry = this.getEntry(uri); + if (!entry) { + throw vscode.FileSystemError.FileNotFound(); + } + + return entry.data; + } + + writeFile(uri: vscode.Uri, content: Uint8Array, { create, overwrite }: { create: boolean; overwrite: boolean }): void { + // console.log('writeFile', uri.toString()); + + const dir = this.getParent(uri); + + const fileName = basename(uri.path); + const dirContents = dir.contents; + + const time = Date.now() / 1000; + const entry = dirContents.get(basename(uri.path)); + if (!entry) { + if (create) { + dirContents.set(fileName, new FsEntry(content, time, time)); + this._emitter.fire([{ type: vscode.FileChangeType.Created, uri }]); + } else { + throw vscode.FileSystemError.FileNotFound(); + } + } else { + if (overwrite) { + entry.mtime = time; + entry.data = content; + this._emitter.fire([{ type: vscode.FileChangeType.Changed, uri }]); + } else { + throw vscode.FileSystemError.NoPermissions('overwrite option was not passed in'); + } + } + } + + rename(_oldUri: vscode.Uri, _newUri: vscode.Uri, _options: { overwrite: boolean }): void { + throw new Error('not implemented'); + } + + delete(uri: vscode.Uri): void { + try { + const dir = this.getParent(uri); + dir.contents.delete(basename(uri.path)); + this._emitter.fire([{ type: vscode.FileChangeType.Deleted, uri }]); + } catch (e) { } + } + + createDirectory(uri: vscode.Uri): void { + // console.log('createDirectory', uri.toString()); + const dir = this.getParent(uri); + const now = Date.now() / 1000; + dir.contents.set(basename(uri.path), new FsEntry(new Map(), now, now)); + } + + private getEntry(uri: vscode.Uri): FsEntry | void { + // TODO: have this throw FileNotFound itself? + // TODO: support configuring case sensitivity + let node: FsEntry = this.root; + for (const component of uri.path.split('/')) { + if (!component) { + // Skip empty components (root, stuff between double slashes, + // trailing slashes) + continue; + } + + if (node.type !== vscode.FileType.Directory) { + // We're looking at a File or such, so bail. + return; + } + + const next = node.contents.get(component); + + if (!next) { + // not found! + return; + } + + node = next; + } + return node; + } + + private getParent(uri: vscode.Uri) { + const dir = this.getEntry(uri.with({ path: dirname(uri.path) })); + if (!dir) { + throw vscode.FileSystemError.FileNotFound(); + } + return dir; + } + + // --- manage file events + + private readonly _emitter = new vscode.EventEmitter(); + + readonly onDidChangeFile: vscode.Event = this._emitter.event; + private readonly watchers = new Map>; + + watch(resource: vscode.Uri): vscode.Disposable { + if (!this.watchers.has(resource.path)) { + this.watchers.set(resource.path, new Set()); + } + const sy = Symbol(resource.path); + return new vscode.Disposable(() => { + const watcher = this.watchers.get(resource.path); + if (watcher) { + watcher.delete(sy); + if (!watcher.size) { + this.watchers.delete(resource.path); + } + } + }); + } +} + +class FsEntry { + get type(): vscode.FileType { + if (this._data instanceof Uint8Array) { + return vscode.FileType.File; + } else { + return vscode.FileType.Directory; + } + } + + get size(): number { + if (this.type === vscode.FileType.Directory) { + return [...this.contents.values()].reduce((acc: number, entry: FsEntry) => acc + entry.size, 0); + } else { + return this.data.length; + } + } + + constructor( + private _data: Uint8Array | Map, + public ctime: number, + public mtime: number, + ) { } + + get data() { + if (this.type === vscode.FileType.Directory) { + throw vscode.FileSystemError.FileIsADirectory; + } + return this._data; + } + set data(val: Uint8Array) { + if (this.type === vscode.FileType.Directory) { + throw vscode.FileSystemError.FileIsADirectory; + } + this._data = val; + } + + get contents() { + if (this.type !== vscode.FileType.Directory) { + throw vscode.FileSystemError.FileNotADirectory; + } + return >this._data; + } +} diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index ab916a1f0e9..c57e6d352c9 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -43,13 +43,15 @@ export class WorkerServerProcessFactory implements TsServerProcessFactory { tsServerLog: TsServerLog | undefined, ) { const tsServerPath = version.tsServerPath; - return new WorkerServerProcess(kind, tsServerPath, this._extensionUri, [ + const launchArgs = [ ...args, - - // Explicitly give TS Server its path so it can - // load local resources + // Explicitly give TS Server its path so it can load local resources '--executingFilePath', tsServerPath, - ], tsServerLog, this._logger); + ]; + if (_configuration.webExperimentalTypeAcquisition) { + launchArgs.push('--experimentalTypeAcquisition'); + } + return new WorkerServerProcess(kind, tsServerPath, this._extensionUri, launchArgs, tsServerLog, this._logger); } } diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index dbed493cc43..59169c18bfd 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../tsconfig.base.json", "compilerOptions": { "outDir": "./out", + "esModuleInterop": true, "experimentalDecorators": true, "types": [ "node" diff --git a/extensions/typescript-language-features/web/README.md b/extensions/typescript-language-features/web/README.md index a9c19b5d72a..eda9e9cb00b 100644 --- a/extensions/typescript-language-features/web/README.md +++ b/extensions/typescript-language-features/web/README.md @@ -1,156 +1,45 @@ # vscode-wasm-typescript -Language server host for typescript using vscode's sync-api in the browser +Language server host for typescript using vscode's sync-api in the browser. -## TODOs +## Getting up and running -### Prototype +To test this out, you'll need three shells: -- [x] get semantic diagnostics rendering squigglies - - typescriptserviceclient.ts has some functions that look at `scheme` to determine some features (hasCapabilityForResource) (also getWorkspaceRootForResource) - - known schemes are in utils/fileSchemes.ts, but don't include vscode-test-web - - adding vscode-test-web in a couple places didn't help, maybe I need to be hackier - - nope, another predicate is `isWeb`, so I had to change place(s) it's used too -- [x] cancellation +1. `yarn watch` for vscode itself +2. `yarn watch-web` for the web side +3. `node /scripts/code-web.js --coi` -### Cleanup +The last command will open a browser window. You'll want to add `?vscode-coi=` +to the end. This is for enabling shared array buffers. So, for example: +`http://localhost:8080/?vscode-coi=`. -- [x] point webpack hack to node_modules; link those files to locally built ones -- [x] create one or more MessageChannels for various communication -- [x] shut down normal listener - - starting the server currently crashes because ts.sys isn't defined -- I think it's a race condition. - In any case it'll need to get shut down before then, which may not be possible without changing Typescript. - - LATER: Turns out you can skip the existing server by depending on tsserverlibrary instead of tsserver. -- [x] figure out a webpack-native way to generate tsserver.web.js if possible -- [x] path rewriting is pretty loosey-goosey; likely to be incorrect some of the time - - invert the logic from TypeScriptServiceClient.normalizedPath for requests - - invert the function from webServer.ts for responses (maybe) - - something with getWorkspaceRootForResource (or anything else that checks `resouce.scheme`) -- [x] put files one level down from virtual root -- [x] fill in missing environment files like lib.dom.d.ts - - toResource's isWeb branch *probably* knows where to find this, just need to put it in the virtual FS - - I guess during setup in serverProcess.browser.ts. - - Not sure whether it needs to have the data or just a fs entry. - - Wait, I don't know how files get added to the FS normally. -- [x] cancellation should only retain one cancellation checker - - the one that matches the current request id - - but that means tracking (or retrieving from tsserver) the request id (aka seq?) - - and correctly setting/resetting it on the cancellation token too. - - I looked at the tsserver code. I think the web case is close to the single-pipe node case, - so I just require that requestId is set in order to call the *current* cancellation checker. - - Any incoming message with a cancellation checker will overwrite the current one. -- [x] Cancellation code in vscode is suspiciously prototypey. - - Specifically, it adds the vscode-wasm cancellation to original cancellation code, but should actually switch to the former for web only. - - looks like `isWeb()` is a way to check for being on the web -- [x] create multiple watchers - - on-demand instead of watching everything and checking on watch firing -- [x] get file watching to work - - it could *already* work, I just don't know how to test it - - look at extensions/markdown-language-features/src/client/fileWatchingManager.ts to see if I can use that - - later: it is OK. its main difference is that you can watch files in not-yet-created directories, and it maintains - a web of directory watches that then check whether the file is eventually created. - - even later: well, it works even though it is similar to my code. - I'm not sure what is different. -- [x] copy fileWatchingManager.ts to web/ ; there's no sharing code between extensions -- [x] Find out scheme the web actually uses instead of vscode-test-web (or switch over entirely to isWeb) -- [x] Need to parse and pass args through so that the syntax server isn't hard-coded to actually be another semantic server -- [x] think about implementing all the other ServerHost methods - - [x] copy importPlugin from previous version of webServer.ts - - [x] also copy details from - - previous implementation (although it's syntax-only so only covers part) - - node implementation in typescript proper -- [x] make realpath support symlinks similarly to node's realpath. - - Johannes says that the filesystem automatically follows symlinks, - so I don't think this is needed. -- [x] organise webServer.ts into multiple files - - OR at least re-arrange it so the diff with the previous version is smaller - - split it into multiple files after the initial PR -- [x] clear out TODOs -- [x] add semicolons everywhere; vscode's lint doesn't seem to complain, but the code clearly uses them -- [x] Further questions about host methods based on existing implementations - - `require` -- is this needed? In TS, it's only used in project system - - `trace` -- is this needed? In TS, it's only used in project system - - `useCaseSensitiveFileNames` -- old version says 'false' is the - safest option, but the virtual fs is case sensitive. Is the old - version still better? - - `writeOutputIsTTY` -- I'm using apiClient.vscode.terminal.write -- is it a tty? - - `getWidthOfTerminal` -- I don't know where to find this on apiClient.vscode.terminal either - - `clearScreen` -- node version writes \x1BC to the terminal. Would - this work for vscode? - - `readFile/writeFile` -- TS handles utf8, utf16le and manually - converts big-endian to utf16 little-endian. How does the in-memory - filesystem handle this? There's no place to specify encoding. (And - `writeFile` currently ignores the flag to write a BOM.) - - `resolvePath` -- node version uses path.resolve. Is it OK to use - that? Or should I re-implement it? Just use identity like the old - web code? - - `getDirectories`/`readDirectory` - - the node code manually skips '.' and '..' in the array returned by - readDirectory. Is this needed? - - `createSHA256Hash` -- the browser version is async, so I skipped it - - `realpath` -- still skips symlinks, I need to figure out what node does +### Working on type acquisition -### Bugs +In order to work with web's new type acquisition, you'll need to enable +`TypeScript > Experimental > Tsserver > Web: Enable Project Wide Intellisense` +in your VS Code options (`Ctrl-,`), you may need to reload the page. -- [x] Response `seq` is always 0. -- [ ] current method of encoding /scheme/authority means that (node) module resolution looks for /scheme/node_modules and /node_modules - - even though they can't possibly exist - - probably not a problem though -- [x] problems pane doesn't clear problems issued on tsconfig. - - This is a known problem in normal usage as well. -- [x] renaming a file throws a No Project error to the console. -- [x] gotodef in another file throws and the editor has a special UI for it. - - definitionProviderBase.getSymbolLocations calls toOpenedFilePath which eventually calls the new / code - - then it calls client.execute which appears to actually request/response to the tsserver - - then the response body is mapped over location.file >> client.toResource >> fromTextSpan - - toResource has isWeb support, as well as (now unused) inMemoryResourcePrefix support - - so I can just redo whatever that did and it'll be fine +This happens when working in a regular `.js` file on a dependency without +declared types. You should be able to open `file.js` and write something like +`import lodash from 'lodash';` at the top of the file and, after a moment, get +types and other intellisense features (like Go To Def/Source Def) working as +expected. This scenario works off Tsserver's own Automatic Type Acquisition +capabilities, and simulates a "global" types cache stored at +`/vscode-global-typings/ts-nul-authority/project`, which is backed by an +in-memory `MemFs` `FileSystemProvider`. -### Done +### Simulated `node_modules` -- [x] need to update 0.2 -> 0.7.* API (once it's working properly) -- [x] including reshuffling the webpack hack if needed -- [x] need to use the settings recommended by Sheetal -- [x] ProjectService always requests a typesMap.json at the cwd -- [x] sync-api-client says fs is rooted at memfs:/sample-folder; the protocol 'memfs:' is confusing our file parsing I think -- [x] nothing ever seems to find tsconfig.json -- [x] messages aren't actually coming through, just the message from the first request - - fixed by simplifying the listener setup for now -- [x] once messages work, you can probably log by postMessage({ type: 'log', body: "some logging text" }) -- [x] implement realpath, modifiedtime, resolvepath, then turn semantic mode on -- [x] file watching implemented with saved map of filename to callback, and forwarding +For regular `.ts` files, instead of going through Tsserver's type acquisition, +a separate `AutoInstallerFs` is used to create a "virtual" `node_modules` that +extracts desired packages on demand, to an underlying `MemFs`. This will +happen any time a filesystem operation is done inside a `node_modules` folder +across any project in the workspace, and will use the "real" `package.json` +(and, if present, `package-lock.json`) to resolve the dependency tree. -### Also - -- [ ] ATA will eventually need a host interface, or an improvement of the existing one (?) - -## Notes - -messages received by extension AND host use paths like ^/memfs/ts-nul-authority/sample-folder/file.ts - -- problem: pretty sure the extension doesn't know what to do with that: it's not putting down error spans in file.ts -- question: why is the extension requesting quickinfo in that URI format? And it works! (probably because the result is a tooltip, not an in-file span) -- problem: weird concatenations with memfs:/ in the middle -- problem: weird concatenations with ^/memfs/ts-nul-authority in the middle - -question: where is the population of sample-folder with a bunch of files happening? - -question: Is that location writable while it's running? - -but readFile is getting called with things like memfs:/sample-folder/memfs:/typesMap.json - directoryExists with /sample-folder/node_modules/@types and /node_modules/@types - same for watchDirectory - watchDirectory with /sample-folder/^ and directoryExists with /sample-folder/^/memfs/ts-nul-authority/sample-folder/workspaces/ - watchFile with /sample-folder/memfs:/sample-folder/memfs:/lib.es2020.full.d.ts - -### LATER - -OK, so the paths that tsserver has look like this: ^/scheme/mount/whatever.ts -but the paths the filesystem has look like this: scheme:/whatever.ts (not sure about 'mount', that's only when cloning from the fs) -so you have to shave off the scheme that the host combined with the path and put on the scheme that the vfs is using. - -### LATER 2 - -Some commands ask for getExecutingFilePath or getCurrentDirectory and cons up a path themselves. -This works, because URI.from({ scheme, path }) matches what the fs has in it -Problem: In *some* messages (all?), vscode then refers to /x.ts and ^/vscode-test-web/mount/x.ts (or ^/memfs/ts-nul-authority/x.ts) +A fallback is then set up such that when a URI like +`memfs:/path/to/node_modules/lodash/lodash.d.ts` is accessed, that gets +redirected to +`vscode-node-modules:/ts-nul-authority/memfs/ts-nul-authority/path/to/node_modules/lodash/lodash.d.ts`, +which will be sent to the `AutoInstallerFs`. diff --git a/extensions/typescript-language-features/web/jsTyping.ts b/extensions/typescript-language-features/web/jsTyping.ts new file mode 100644 index 00000000000..bd940e88c1c --- /dev/null +++ b/extensions/typescript-language-features/web/jsTyping.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +/// Utilities copied from ts.JsTyping internals + +export const enum NameValidationResult { + Ok, + EmptyName, + NameTooLong, + NameStartsWithDot, + NameStartsWithUnderscore, + NameContainsNonURISafeCharacters +} + +type PackageNameValidationResult = NameValidationResult | ScopedPackageNameValidationResult; + +interface ScopedPackageNameValidationResult { + readonly name: string; + readonly isScopeName: boolean; + readonly result: NameValidationResult; +} + +enum CharacterCodes { + _ = 0x5F, + dot = 0x2E, +} + +const maxPackageNameLength = 214; + +// Validates package name using rules defined at https://docs.npmjs.com/files/package.json +// Copied from typescript/jsTypings.ts +export function validatePackageNameWorker(packageName: string, supportScopedPackage: true): ScopedPackageNameValidationResult; +export function validatePackageNameWorker(packageName: string, supportScopedPackage: false): NameValidationResult; +export function validatePackageNameWorker(packageName: string, supportScopedPackage: boolean): PackageNameValidationResult { + if (!packageName) { + return NameValidationResult.EmptyName; + } + if (packageName.length > maxPackageNameLength) { + return NameValidationResult.NameTooLong; + } + if (packageName.charCodeAt(0) === CharacterCodes.dot) { + return NameValidationResult.NameStartsWithDot; + } + if (packageName.charCodeAt(0) === CharacterCodes._) { + return NameValidationResult.NameStartsWithUnderscore; + } + + // check if name is scope package like: starts with @ and has one '/' in the middle + // scoped packages are not currently supported + if (supportScopedPackage) { + const matches = /^@([^/]+)\/([^/]+)$/.exec(packageName); + if (matches) { + const scopeResult = validatePackageNameWorker(matches[1], /*supportScopedPackage*/ false); + if (scopeResult !== NameValidationResult.Ok) { + return { name: matches[1], isScopeName: true, result: scopeResult }; + } + const packageResult = validatePackageNameWorker(matches[2], /*supportScopedPackage*/ false); + if (packageResult !== NameValidationResult.Ok) { + return { name: matches[2], isScopeName: false, result: packageResult }; + } + return NameValidationResult.Ok; + } + } + + if (encodeURIComponent(packageName) !== packageName) { + return NameValidationResult.NameContainsNonURISafeCharacters; + } + + return NameValidationResult.Ok; +} + +export interface TypingResolutionHost { + directoryExists(path: string): boolean; + fileExists(fileName: string): boolean; + readFile(path: string, encoding?: string): string | undefined; + readDirectory(rootDir: string, extensions: readonly string[], excludes: readonly string[] | undefined, includes: readonly string[] | undefined, depth?: number): string[]; +} diff --git a/extensions/typescript-language-features/web/tsconfig.json b/extensions/typescript-language-features/web/tsconfig.json index 9944d5b63d8..531d57bddcb 100644 --- a/extensions/typescript-language-features/web/tsconfig.json +++ b/extensions/typescript-language-features/web/tsconfig.json @@ -2,8 +2,7 @@ "extends": "../../tsconfig.base.json", "compilerOptions": { "outDir": "../../out", - "module": "nodenext", - "moduleDetection": "legacy", + "esModuleInterop": true, "experimentalDecorators": true, "types": [ "node" diff --git a/extensions/typescript-language-features/web/typingsInstaller.ts b/extensions/typescript-language-features/web/typingsInstaller.ts new file mode 100644 index 00000000000..7b9b164c40c --- /dev/null +++ b/extensions/typescript-language-features/web/typingsInstaller.ts @@ -0,0 +1,213 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* + * This file implements the global typings installer API for web clients. It + * uses [nassun](https://docs.rs/nassun) and + * [node-maintainer](https://docs.rs/node-maintainer) to install typings + * in-memory (and maybe eventually cache them in IndexedDB?). + * + * Implementing a typings installer involves implementing two parts: + * + * -> ITypingsInstaller: the "top level" interface that tsserver uses to + * request typings. Implementers of this interface are what actually get + * passed to tsserver. + * + * -> TypingsInstaller: an abstract class that implements a good chunk of + * the "generic" functionality for what ITypingsInstaller needs to do. For + * implementation detail reasons, it does this in a "server/client" model of + * sorts. In our case, we don't need a separate process, or even _quite_ a + * pure "server/client" model, so we play along a bit for the sake of reusing + * the stuff the abstract class is already doing for us. + */ + +import { PackageManager, PackageType } from '@vscode/ts-package-manager'; +import { join } from 'path'; +import * as ts from 'typescript/lib/tsserverlibrary'; +import { NameValidationResult, validatePackageNameWorker } from './jsTyping'; + +type InstallerResponse = ts.server.PackageInstalledResponse | ts.server.SetTypings | ts.server.InvalidateCachedTypings | ts.server.BeginInstallTypes | ts.server.EndInstallTypes | ts.server.WatchTypingLocations; + +/** + * The "server" part of the "server/client" model. This is the part that + * actually gets instantiated and passed to tsserver. + */ +export default class WebTypingsInstallerClient implements ts.server.ITypingsInstaller { + + private projectService: ts.server.ProjectService | undefined; + + private requestedRegistry = false; + + private typesRegistryCache: Map> = new Map(); + + private readonly server: Promise; + + constructor( + private readonly fs: ts.server.ServerHost, + readonly globalTypingsCacheLocation: string, + ) { + this.server = WebTypingsInstallerServer.initialize( + (response: InstallerResponse) => this.handleResponse(response), + this.fs, + globalTypingsCacheLocation + ); + } + + /** + * TypingsInstaller expects a "server/client" model, and as such, some of + * its methods are implemented in terms of sending responses back to a + * client. This method is a catch-all for those responses generated by + * TypingsInstaller internals. + */ + private async handleResponse(response: InstallerResponse): Promise { + switch (response.kind) { + case 'action::packageInstalled': + case 'action::invalidate': + case 'action::set': + this.projectService!.updateTypingsForProject(response); + break; + case 'event::beginInstallTypes': + case 'event::endInstallTypes': + // Don't care. + break; + default: + throw new Error(`unexpected response: ${response}`); + } + } + + // NB(kmarchan): this is a code action that expects an actual NPM-specific + // installation. We shouldn't mess with this ourselves. + async installPackage(_options: ts.server.InstallPackageOptionsWithProject): Promise { + throw new Error('not implemented'); + } + + // NB(kmarchan): As far as I can tell, this is only ever used for + // completions? + isKnownTypesPackageName(packageName: string): boolean { + console.log('isKnownTypesPackageName', packageName); + const looksLikeValidName = validatePackageNameWorker(packageName, true); + if (looksLikeValidName.result !== NameValidationResult.Ok) { + return false; + } + + if (this.requestedRegistry) { + return !!this.typesRegistryCache && this.typesRegistryCache.has(packageName); + } + + this.requestedRegistry = true; + this.server.then(s => this.typesRegistryCache = s.typesRegistry); + return false; + } + + enqueueInstallTypingsRequest(p: ts.server.Project, typeAcquisition: ts.TypeAcquisition, unresolvedImports: ts.SortedReadonlyArray): void { + console.log('enqueueInstallTypingsRequest', typeAcquisition, unresolvedImports); + const req = ts.server.createInstallTypingsRequest(p, typeAcquisition, unresolvedImports); + this.server.then(s => s.install(req)); + } + + attach(projectService: ts.server.ProjectService): void { + this.projectService = projectService; + } + + onProjectClosed(_projectService: ts.server.Project): void { + // noop + } +} + +/** + * Internal implementation of the "server" part of the "server/client" model. + * This takes advantage of the existing TypingsInstaller to reuse a lot of + * already-implemented logic around package installation, but with + * installation details handled by Nassun/Node Maintainer. + */ +class WebTypingsInstallerServer extends ts.server.typingsInstaller.TypingsInstaller { + + private static readonly typesRegistryPackageName = 'types-registry'; + + private constructor( + override typesRegistry: Map>, + private readonly handleResponse: (response: InstallerResponse) => void, + fs: ts.server.ServerHost, + private readonly packageManager: PackageManager, + globalTypingsCachePath: string, + ) { + super(fs, globalTypingsCachePath, join(globalTypingsCachePath, 'fakeSafeList') as ts.Path, join(globalTypingsCachePath, 'fakeTypesMapLocation') as ts.Path, Infinity); + } + + /** + * Because loading the typesRegistry is an async operation for us, we need + * to have a separate "constructor" that will be used by + * WebTypingsInstallerClient. + * + * @returns a promise that resolves to a WebTypingsInstallerServer + */ + static async initialize( + handleResponse: (response: InstallerResponse) => void, + fs: ts.server.ServerHost, + globalTypingsCachePath: string, + ): Promise { + const pm = new PackageManager(fs); + const pkgJson = join(globalTypingsCachePath, 'package.json'); + if (!fs.fileExists(pkgJson)) { + fs.writeFile(pkgJson, '{"private":true}'); + } + const resolved = await pm.resolveProject(globalTypingsCachePath, { + addPackages: [this.typesRegistryPackageName] + }); + await resolved.restore(); + + const registry = new Map>(); + const indexPath = join(globalTypingsCachePath, 'node_modules/types-registry/index.json'); + const index = WebTypingsInstallerServer.readJson(fs, indexPath); + for (const [packageName, entry] of Object.entries(index.entries)) { + registry.set(packageName, entry as ts.MapLike); + } + console.log('ATA registry loaded'); + return new WebTypingsInstallerServer(registry, handleResponse, fs, pm, globalTypingsCachePath); + } + + /** + * Implements the actual logic of installing a set of given packages. It + * does this by looking up the latest versions of those packages using + * Nassun, then handing Node Maintainer the updated package.json to run a + * full install (modulo existing lockfiles, which can make this faster). + */ + protected override installWorker(requestId: number, packageNames: string[], cwd: string, onRequestCompleted: ts.server.typingsInstaller.RequestCompletedAction): void { + console.log('installWorker', requestId, cwd); + (async () => { + try { + const resolved = await this.packageManager.resolveProject(cwd, { + addPackages: packageNames, + packageType: PackageType.DevDependency + }); + await resolved.restore(); + onRequestCompleted(true); + } catch (e) { + onRequestCompleted(false); + } + })(); + } + + /** + * This is a thing that TypingsInstaller uses internally to send + * responses, and we'll need to handle this in the Client later. + */ + protected override sendResponse(response: InstallerResponse): void { + this.handleResponse(response); + } + + /** + * What it says on the tin. Reads a JSON file from the given path. Throws + * if the file doesn't exist (as opposed to returning `undefined`, like + * fs.readFile does). + */ + private static readJson(fs: ts.server.ServerHost, path: string): any { + const data = fs.readFile(path); + if (!data) { + throw new Error('Failed to read file: ' + path); + } + return JSON.parse(data.trim()); + } +} diff --git a/extensions/typescript-language-features/web/webServer.ts b/extensions/typescript-language-features/web/webServer.ts index 1688a93fcc0..191c2d03f63 100644 --- a/extensions/typescript-language-features/web/webServer.ts +++ b/extensions/typescript-language-features/web/webServer.ts @@ -5,10 +5,12 @@ /// /// -import * as ts from 'typescript/lib/tsserverlibrary'; -import { ApiClient, FileType, Requests } from '@vscode/sync-api-client'; +import { ApiClient, FileStat, FileSystem, FileType, Requests } from '@vscode/sync-api-client'; import { ClientConnection } from '@vscode/sync-api-common/browser'; +import { basename } from 'path'; +import * as ts from 'typescript/lib/tsserverlibrary'; import { URI } from 'vscode-uri'; +import WebTypingsInstaller from './typingsInstaller'; // GLOBALS const watchFiles: Map = new Map(); @@ -87,7 +89,7 @@ class AccessOutsideOfRootError extends Error { type ServerHostWithImport = ts.server.ServerHost & { importPlugin(root: string, moduleName: string): Promise }; -function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient: ApiClient | undefined, args: string[], fsWatcher: MessagePort): ServerHostWithImport { +function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient: ApiClient | undefined, args: string[], fsWatcher: MessagePort, enabledExperimentalTypeAcquisition: boolean): ServerHostWithImport { const currentDirectory = '/'; const fs = apiClient?.vscode.workspace.fileSystem; let watchId = 0; @@ -99,7 +101,6 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient const directorySeparator: string = (ts as any).directorySeparator; const executingFilePath = findArgument(args, '--executingFilePath') || location + ''; const getExecutingDirectoryPath = memoize(() => memoize(() => ensureTrailingDirectorySeparator(getDirectoryPath(executingFilePath)))); - // Later we could map ^memfs:/ to do something special if we want to enable more functionality like module resolution or something like that const getWebPath = (path: string) => path.startsWith(directorySeparator) ? path.replace(directorySeparator, getExecutingDirectoryPath()) : undefined; const textDecoder = new TextDecoder(); @@ -121,6 +122,8 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient return noopWatcher; } + console.log('watching file:', path); + logVerbose('fs.watchFile', { path }); let uri: URI; @@ -132,14 +135,20 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient } watchFiles.set(path, { path, callback, pollingInterval, options }); - watchId++; - fsWatcher.postMessage({ type: 'watchFile', uri, id: watchId }); + const watchIds = [++watchId]; + fsWatcher.postMessage({ type: 'watchFile', uri: uri, id: watchIds[0] }); + if (enabledExperimentalTypeAcquisition && looksLikeNodeModules(path)) { + watchIds.push(++watchId); + fsWatcher.postMessage({ type: 'watchFile', uri: mapUri(uri, 'vscode-node-modules'), id: watchIds[1] }); + } return { close() { logVerbose('fs.watchFile.close', { path }); watchFiles.delete(path); - fsWatcher.postMessage({ type: 'dispose', id: watchId }); + for (const id of watchIds) { + fsWatcher.postMessage({ type: 'dispose', id }); + } } }; }, @@ -155,14 +164,16 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient } watchDirectories.set(path, { path, callback, recursive, options }); - watchId++; + const watchIds = [++watchId]; fsWatcher.postMessage({ type: 'watchDirectory', recursive, uri, id: watchId }); return { close() { logVerbose('fs.watchDirectory.close', { path }); watchDirectories.delete(path); - fsWatcher.postMessage({ type: 'dispose', id: watchId }); + for (const id of watchIds) { + fsWatcher.postMessage({ type: 'dispose', id }); + } } }; }, @@ -226,14 +237,28 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient } } + let uri; try { - // We need to slice the bytes since we can't pass a shared array to text decoder - const contents = fs.readFile(toResource(path)).slice(); - return textDecoder.decode(contents); - } catch (error) { - logNormal('Error fs.readFile', { path, error: error + '' }); + uri = toResource(path); + } catch (e) { return undefined; } + + let contents: Uint8Array | undefined; + try { + // We need to slice the bytes since we can't pass a shared array to text decoder + contents = fs.readFile(uri); + } catch (error) { + if (!enabledExperimentalTypeAcquisition) { + return undefined; + } + try { + contents = fs.readFile(mapUri(uri, 'vscode-node-modules')); + } catch (e) { + return undefined; + } + } + return textDecoder.decode(contents.slice()); }, getFileSize(path) { logVerbose('fs.getFileSize', { path }); @@ -242,12 +267,19 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient throw new Error('not supported'); } + const uri = toResource(path); + let ret = 0; try { - return fs.stat(toResource(path)).size; - } catch (error) { - logNormal('Error fs.getFileSize', { path, error: error + '' }); - return 0; + ret = fs.stat(uri).size; + } catch (_error) { + if (enabledExperimentalTypeAcquisition) { + try { + ret = fs.stat(mapUri(uri, 'vscode-node-modules')).size; + } catch (_error) { + } + } } + return ret; }, writeFile(path, data, writeByteOrderMark) { logVerbose('fs.writeFile', { path }); @@ -260,10 +292,21 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient data = byteOrderMarkIndicator + data; } + let uri; try { - fs.writeFile(toResource(path), textEncoder.encode(data)); + uri = toResource(path); + } catch (e) { + return; + } + const encoded = textEncoder.encode(data); + try { + fs.writeFile(uri, encoded); + const name = basename(uri.path); + if (uri.scheme !== 'vscode-global-typings' && (name === 'package.json' || name === 'package-lock.json' || name === 'package-lock.kdl')) { + fs.writeFile(mapUri(uri, 'vscode-node-modules'), encoded); + } } catch (error) { - logNormal('Error fs.writeFile', { path, error: error + '' }); + console.error('fs.writeFile', { path, error }); } }, resolvePath(path: string): string { @@ -284,12 +327,24 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient return request.status === 200; } + let uri; try { - return fs.stat(toResource(path)).type === FileType.File; - } catch (error) { - logNormal('Error fs.fileExists', { path, error: error + '' }); + uri = toResource(path); + } catch (e) { return false; } + let ret = false; + try { + ret = fs.stat(uri).type === FileType.File; + } catch (_error) { + if (enabledExperimentalTypeAcquisition) { + try { + ret = fs.stat(mapUri(uri, 'vscode-node-modules')).type === FileType.File; + } catch (_error) { + } + } + } + return ret; }, directoryExists(path: string): boolean { logVerbose('fs.directoryExists', { path }); @@ -298,10 +353,32 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient return false; } + let uri; try { - return fs.stat(toResource(path)).type === FileType.Directory; - } catch (error) { - logNormal('Error fs.directoryExists', { path, error: error + '' }); + uri = toResource(path); + } catch (_error) { + return false; + } + + let stat: FileStat | undefined = undefined; + try { + stat = fs.stat(uri); + } catch (_error) { + if (enabledExperimentalTypeAcquisition) { + try { + stat = fs.stat(mapUri(uri, 'vscode-node-modules')); + } catch (_error) { + } + } + } + if (stat) { + if (path.startsWith('/https') && !path.endsWith('.d.ts')) { + // TODO: Hack, https "file system" can't actually tell what is a file vs directory + return stat.type === FileType.File || stat.type === FileType.Directory; + } + + return stat.type === FileType.Directory; + } else { return false; } }, @@ -341,12 +418,19 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient throw new Error('not supported'); } + const uri = toResource(path); + let s: FileStat | undefined = undefined; try { - return new Date(fs.stat(toResource(path)).mtime); - } catch (error) { - logNormal('Error fs.getModifiedTime', { path, error: error + '' }); - return undefined; + s = fs.stat(uri); + } catch (_e) { + if (enabledExperimentalTypeAcquisition) { + try { + s = fs.stat(mapUri(uri, 'vscode-node-modules')); + } catch (_e) { + } + } } + return s && new Date(s.mtime); }, deleteFile(path: string): void { logVerbose('fs.deleteFile', { path }); @@ -373,13 +457,19 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient base64encode: input => Buffer.from(input).toString('base64'), }; - /** For module resolution only; symlinks aren't supported yet. */ + // For module resolution only. `node_modules` is also automatically mapped + // as if all node_modules-like paths are symlinked. function realpath(path: string): string { - // skip paths without .. or ./ or /. - if (!path.match(/\.\.|\/\.|\.\//)) { + const isNm = looksLikeNodeModules(path) && !path.startsWith('/vscode-global-typings/'); + // skip paths without .. or ./ or /. And things that look like node_modules + if (!isNm && !path.match(/\.\.|\/\.|\.\//)) { return path; } - const uri = toResource(path); + + let uri = toResource(path); + if (isNm) { + uri = mapUri(uri, 'vscode-node-modules'); + } const out = [uri.scheme]; if (uri.authority) { out.push(uri.authority); } for (const part of uri.path.split('/')) { @@ -403,31 +493,35 @@ function createServerHost(extensionUri: URI, logger: ts.server.Logger, apiClient throw new Error('not supported'); } + const uri = toResource(path || '.'); + let entries: [string, FileType][] = []; + const files: string[] = []; + const directories: string[] = []; try { - const uri = toResource(path || '.'); - const entries = fs.readDirectory(uri); - const files: string[] = []; - const directories: string[] = []; - for (const [entry, type] of entries) { - // This is necessary because on some file system node fails to exclude - // '.' and '..'. See https://github.com/nodejs/node/issues/4002 - if (entry === '.' || entry === '..') { - continue; - } - - if (type === FileType.File) { - files.push(entry); - } - else if (type === FileType.Directory) { - directories.push(entry); - } + entries = fs.readDirectory(uri); + } catch (_e) { + try { + entries = fs.readDirectory(mapUri(uri, 'vscode-node-modules')); + } catch (_e) { } - files.sort(); - directories.sort(); - return { files, directories }; - } catch (e) { - return { files: [], directories: [] }; } + for (const [entry, type] of entries) { + // This is necessary because on some file system node fails to exclude + // '.' and '..'. See https://github.com/nodejs/node/issues/4002 + if (entry === '.' || entry === '..') { + continue; + } + + if (type === FileType.File) { + files.push(entry); + } + else if (type === FileType.Directory) { + directories.push(entry); + } + } + files.sort(); + directories.sort(); + return { files, directories }; } /** @@ -475,6 +569,10 @@ function looksLikeLibDtsPath(filepath: string) { return filepath.startsWith('/lib.') && filepath.endsWith('.d.ts'); } +function looksLikeNodeModules(filepath: string) { + return filepath.includes('/node_modules'); +} + function filePathToResourceUri(filepath: string): URI | undefined { const parts = filepath.match(/^\/([^\/]+)\/([^\/]*)(?:\/(.+))?$/); if (!parts) { @@ -509,14 +607,15 @@ class WasmCancellationToken implements ts.server.ServerCancellationToken { } interface StartSessionOptions { - globalPlugins: ts.server.SessionOptions['globalPlugins']; - pluginProbeLocations: ts.server.SessionOptions['pluginProbeLocations']; - allowLocalPluginLoads: ts.server.SessionOptions['allowLocalPluginLoads']; - useSingleInferredProject: ts.server.SessionOptions['useSingleInferredProject']; - useInferredProjectPerProjectRoot: ts.server.SessionOptions['useInferredProjectPerProjectRoot']; - suppressDiagnosticEvents: ts.server.SessionOptions['suppressDiagnosticEvents']; - noGetErrOnBackgroundUpdate: ts.server.SessionOptions['noGetErrOnBackgroundUpdate']; - serverMode: ts.server.SessionOptions['serverMode']; + readonly globalPlugins: ts.server.SessionOptions['globalPlugins']; + readonly pluginProbeLocations: ts.server.SessionOptions['pluginProbeLocations']; + readonly allowLocalPluginLoads: ts.server.SessionOptions['allowLocalPluginLoads']; + readonly useSingleInferredProject: ts.server.SessionOptions['useSingleInferredProject']; + readonly useInferredProjectPerProjectRoot: ts.server.SessionOptions['useInferredProjectPerProjectRoot']; + readonly suppressDiagnosticEvents: ts.server.SessionOptions['suppressDiagnosticEvents']; + readonly noGetErrOnBackgroundUpdate: ts.server.SessionOptions['noGetErrOnBackgroundUpdate']; + readonly serverMode: ts.server.SessionOptions['serverMode']; + readonly disableAutomaticTypingAcquisition: boolean; } class WorkerSession extends ts.server.Session<{}> { @@ -526,17 +625,20 @@ class WorkerSession extends ts.server.Session<{}> { constructor( host: ts.server.ServerHost, + fs: FileSystem | undefined, options: StartSessionOptions, - public readonly port: MessagePort, + private readonly port: MessagePort, logger: ts.server.Logger, hrtime: ts.server.SessionOptions['hrtime'] ) { const cancellationToken = new WasmCancellationToken(); + const typingsInstaller = options.disableAutomaticTypingAcquisition || !fs ? ts.server.nullTypingsInstaller : new WebTypingsInstaller(host, '/vscode-global-typings/ts-nul-authority/projects'); + super({ host, cancellationToken, ...options, - typingsInstaller: ts.server.nullTypingsInstaller, // TODO: Someday! + typingsInstaller, byteLength: () => { throw new Error('Not implemented'); }, // Formats the message text in send of Session which is overridden in this class so not needed hrtime, logger, @@ -671,7 +773,7 @@ async function initializeSession(args: string[], extensionUri: URI, ports: { tss logger.info(`Version: 0.0.0`); logger.info(`Arguments: ${args.join(' ')}`); logger.info(`ServerMode: ${serverMode} unknownServerMode: ${unknownServerMode}`); - const options = { + const options: StartSessionOptions = { globalPlugins: findArgumentStringArray(args, '--globalPlugins'), pluginProbeLocations: findArgumentStringArray(args, '--pluginProbeLocations'), allowLocalPluginLoads: hasArgument(args, '--allowLocalPluginLoads'), @@ -679,22 +781,27 @@ async function initializeSession(args: string[], extensionUri: URI, ports: { tss useInferredProjectPerProjectRoot: hasArgument(args, '--useInferredProjectPerProjectRoot'), suppressDiagnosticEvents: hasArgument(args, '--suppressDiagnosticEvents'), noGetErrOnBackgroundUpdate: hasArgument(args, '--noGetErrOnBackgroundUpdate'), - serverMode + serverMode, + disableAutomaticTypingAcquisition: hasArgument(args, '--disableAutomaticTypingAcquisition'), }; + let sys: ServerHostWithImport; + let fs: FileSystem | undefined; if (hasArgument(args, '--enableProjectWideIntelliSenseOnWeb')) { + const enabledExperimentalTypeAcquisition = hasArgument(args, '--experimentalTypeAcquisition'); const connection = new ClientConnection(ports.sync); await connection.serviceReady(); - sys = createServerHost(extensionUri, logger, new ApiClient(connection), args, ports.watcher); + const apiClient = new ApiClient(connection); + fs = apiClient.vscode.workspace.fileSystem; + sys = createServerHost(extensionUri, logger, apiClient, args, ports.watcher, enabledExperimentalTypeAcquisition); } else { - sys = createServerHost(extensionUri, logger, undefined, args, ports.watcher); - + sys = createServerHost(extensionUri, logger, undefined, args, ports.watcher, false); } setSys(sys); - session = new WorkerSession(sys, options, ports.tsserver, logger, hrtime); + session = new WorkerSession(sys, fs, options, ports.tsserver, logger, hrtime); session.listen(); } @@ -743,3 +850,15 @@ const listener = async (e: any) => { console.error(`unexpected message on main channel: ${JSON.stringify(e)}`); }; addEventListener('message', listener); + +function mapUri(uri: URI, mappedScheme: string): URI { + if (uri.scheme === 'vscode-global-typings') { + throw new Error('can\'t map vscode-global-typings'); + } + if (!uri.authority) { + uri = uri.with({ authority: 'ts-nul-authority' }); + } + uri = uri.with({ scheme: mappedScheme, path: `/${uri.scheme}/${uri.authority || 'ts-nul-authority'}${uri.path}` }); + + return uri; +} diff --git a/extensions/typescript-language-features/yarn.lock b/extensions/typescript-language-features/yarn.lock index eedf2d9c580..710e6c54132 100644 --- a/extensions/typescript-language-features/yarn.lock +++ b/extensions/typescript-language-features/yarn.lock @@ -9,15 +9,7 @@ dependencies: tslib "^2.2.0" -"@azure/core-auth@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.4.0.tgz#6fa9661c1705857820dbc216df5ba5665ac36a9e" - integrity sha512-HFrcTgmuSuukRf/EdPmqBrc5l6Q5Uu+2TbuhaKbgaCpP2TfAeiNaQPAadxO+CYBRHGUzIDteMAjFspFLDLnKVQ== - dependencies: - "@azure/abort-controller" "^1.0.0" - tslib "^2.2.0" - -"@azure/core-auth@^1.5.0": +"@azure/core-auth@^1.4.0", "@azure/core-auth@^1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== @@ -57,15 +49,7 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-util@^1.0.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" - integrity sha512-A4TBYVQCtHOigFb2ETiiKFDocBoI1Zk2Ui1KpI42aJSIDexF7DHQFpnjonltXAIU/ceH+1fsZAWWgvX6/AKzog== - dependencies: - "@azure/abort-controller" "^1.0.0" - tslib "^2.2.0" - -"@azure/core-util@^1.1.0": +"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== @@ -74,9 +58,9 @@ tslib "^2.2.0" "@azure/logger@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" - integrity sha512-aK4s3Xxjrx3daZr3VylxejK3vG5ExXck5WOHDJ8in/k9AqlfIyFMMT1uG7u8mNjX+QRILTIn0/Xgschfh/dQ9g== + version "1.0.4" + resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.4.tgz#28bc6d0e5b3c38ef29296b32d35da4e483593fa1" + integrity sha512-ustrPY8MryhloQj7OWGe+HrYx+aoiOxzbXTtgblbV3xwCqpzUK36phH3XNHQKj3EPonyFUuDTfR3qFhTEAuZEg== dependencies: tslib "^2.2.0" @@ -180,12 +164,7 @@ resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-snippet/-/applicationinsights-web-snippet-1.0.1.tgz#6bb788b2902e48bf5d460c38c6bb7fedd686ddd7" integrity sha512-2IHAOaLauc8qaAitvWS+U931T+ze+7MNWrDHY47IENP5y2UA0vqJDu67kWZDdpCN1fFC77sfgfB+HV7SrKshnQ== -"@microsoft/dynamicproto-js@^1.1.7": - version "1.1.7" - resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" - integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== - -"@microsoft/dynamicproto-js@^1.1.9": +"@microsoft/dynamicproto-js@^1.1.7", "@microsoft/dynamicproto-js@^1.1.9": version "1.1.9" resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== @@ -260,9 +239,9 @@ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== "@types/node@18.x": - version "18.15.13" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" - integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== + version "18.17.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.11.tgz#c04054659d88bfeba94095f41ef99a8ddf4e1813" + integrity sha512-r3hjHPBu+3LzbGBa8DHnr/KAeTEEOrahkcL+cZc4MaBMTM+mk8LtXR+zw+nqfjuDZZzYTYgTcpHuP+BEQk069g== "@types/semver@^5.5.0": version "5.5.0" @@ -305,6 +284,11 @@ "@vscode/sync-api-common" "0.7.2" vscode-uri "3.0.3" +"@vscode/ts-package-manager@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@vscode/ts-package-manager/-/ts-package-manager-0.0.2.tgz#d1cade5ff0d01da8c5b5b00bf79d80e7156771cf" + integrity sha512-cXPxGbPVTkEQI8mUiWYUwB6j3ga6M9i7yubUOCrjgZ01GeZPMSnaWRprfJ09uuy81wJjY2gfHgLsOgwrGvUBTw== + acorn-import-assertions@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" @@ -429,9 +413,9 @@ emitter-listener@^1.0.1, emitter-listener@^1.1.1: shimmer "^1.2.0" follow-redirects@^1.14.8: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== form-data@^4.0.0: version "4.0.0" @@ -587,9 +571,9 @@ tas-client@0.1.58: axios "^0.26.1" tslib@^2.2.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" - integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== uuid@^8.3.0: version "8.3.2" @@ -603,11 +587,16 @@ vscode-tas-client@^0.1.63: dependencies: tas-client "0.1.58" -vscode-uri@3.0.3, vscode-uri@^3.0.3: +vscode-uri@3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== +vscode-uri@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.7.tgz#6d19fef387ee6b46c479e5fb00870e15e58c1eb8" + integrity sha512-eOpPHogvorZRobNqJGhapa0JdwaxpjVvyBp0QIUMRMSf8ZAlqOdEquKuRmw9Qwu0qXtJIWqFtMkmvJjUZmMjVA== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" From a2be681a700076aeb73e25b33f083c5a6ceb46fb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 28 Aug 2023 09:50:20 +0200 Subject: [PATCH 252/607] simplifying the code --- .../browser/stickyScrollWidget.ts | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 00496bec0f0..e4d843f0551 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -45,6 +45,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _lineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; private _minContentWidthInPx: number = 0; + private _isOnFoldingGlyphMargin: boolean = false; constructor( private readonly _editor: ICodeEditor @@ -187,28 +188,29 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return; } this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, (e) => { - const mouseEventTriggerredByClick = - 'fromElement' in e - && e.fromElement instanceof HTMLElement - && e.fromElement.classList.contains('codicon'); + + this._isOnFoldingGlyphMargin = true; for (const line of this._stickyLines) { const foldingIcon = line.foldingIcon; if (!foldingIcon) { continue; } - if (mouseEventTriggerredByClick) { - foldingIcon.setTransitionRequired(false); - foldingIcon.setVisible(true); - setTimeout(() => { foldingIcon.setTransitionRequired(true); }, 300); - } else { - foldingIcon.setVisible(true); - } + foldingIcon.setTransitionRequired(true); + foldingIcon.setVisible(true); + foldingIcon.setTransitionRequired(false); } })); this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_LEAVE, () => { + + this._isOnFoldingGlyphMargin = false; + for (const line of this._stickyLines) { const foldingIcon = line.foldingIcon; + if (!foldingIcon) { + continue; + } + foldingIcon.setTransitionRequired(true); foldingIcon?.setVisible(foldingIcon.isCollapsed); } })); From 722a48ba53553da5b5325b67c608634f43a39a2a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sun, 27 Aug 2023 00:18:00 +0200 Subject: [PATCH 253/607] Fixes #191251, makes bread crumb items clickable --- .../diffEditorWidget2/diffEditorViewModel.ts | 28 ++++++++++++--- .../widget/diffEditorWidget2/style.css | 8 +++++ .../diffEditorWidget2/unchangedRanges.ts | 36 ++++++++++++------- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index a41ba6a9909..039f82dd6ac 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -209,27 +209,27 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo })); } - public ensureModifiedLineIsVisible(lineNumber: number, tx: ITransaction): void { + public ensureModifiedLineIsVisible(lineNumber: number, tx: ITransaction | undefined): void { if (this.diff.get()?.mappings.length === 0) { return; } const unchangedRegions = this._unchangedRegions.get().regions; for (const r of unchangedRegions) { if (r.getHiddenModifiedRange(undefined).contains(lineNumber)) { - r.showAll(tx); // TODO only unhide what is needed + r.showModifiedLine(lineNumber, tx); return; } } } - public ensureOriginalLineIsVisible(lineNumber: number, tx: ITransaction): void { + public ensureOriginalLineIsVisible(lineNumber: number, tx: ITransaction | undefined): void { if (this.diff.get()?.mappings.length === 0) { return; } const unchangedRegions = this._unchangedRegions.get().regions; for (const r of unchangedRegions) { if (r.getHiddenOriginalRange(undefined).contains(lineNumber)) { - r.showAll(tx); // TODO only unhide what is needed + r.showOriginalLine(lineNumber, tx); return; } } @@ -421,6 +421,26 @@ export class UnchangedRegion { this._visibleLineCountBottom.set(this.lineCount - this._visibleLineCountTop.get(), tx); } + public showModifiedLine(lineNumber: number, tx: ITransaction | undefined): void { + const top = lineNumber + 1 - (this.modifiedLineNumber + this._visibleLineCountTop.get()); + const bottom = (this.modifiedLineNumber - this._visibleLineCountBottom.get() + this.lineCount) - lineNumber; + if (top < bottom) { + this._visibleLineCountTop.set(this._visibleLineCountTop.get() + top, tx); + } else { + this._visibleLineCountBottom.set(this._visibleLineCountBottom.get() + bottom, tx); + } + } + + public showOriginalLine(lineNumber: number, tx: ITransaction | undefined): void { + const top = lineNumber - this.originalLineNumber; + const bottom = (this.originalLineNumber + this.lineCount) - lineNumber; + if (top < bottom) { + this._visibleLineCountTop.set(Math.min(this._visibleLineCountTop.get() + bottom - top, this.getMaxVisibleLineCountTop()), tx); + } else { + this._visibleLineCountBottom.set(Math.min(this._visibleLineCountBottom.get() + top - bottom, this.getMaxVisibleLineCountBottom()), tx); + } + } + public setState(visibleLineCountTop: number, visibleLineCountBottom: number, tx: ITransaction | undefined): void { visibleLineCountTop = Math.max(Math.min(visibleLineCountTop, this.lineCount), 0); visibleLineCountBottom = Math.max(Math.min(visibleLineCountBottom, this.lineCount - visibleLineCountTop), 0); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/style.css b/src/vs/editor/browser/widget/diffEditorWidget2/style.css index 80cd4921c25..93d6279c883 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/style.css +++ b/src/vs/editor/browser/widget/diffEditorWidget2/style.css @@ -74,6 +74,14 @@ color: var(--vscode-editorLink-activeForeground) !important; } +.monaco-editor .diff-hidden-lines div.breadcrumb-item { + cursor: pointer; +} + +.monaco-editor .diff-hidden-lines div.breadcrumb-item:hover { + color: var(--vscode-editorLink-activeForeground); +} + .monaco-editor .movedOriginal { border: 2px solid var(--vscode-diffEditor-move-border); } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 520a673300b..33d9ed81582 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -94,13 +94,13 @@ export class UnchangedRangesFeature extends Disposable { const d = derived(reader => /** @description hiddenOriginalRangeStart */ r.getHiddenOriginalRange(reader).startLineNumber - 1); const origVz = new PlaceholderViewZone(d, 24); origViewZones.push(origVz); - store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource)); + store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource, l => this._diffModel.get()!.ensureOriginalLineIsVisible(l, undefined))); } { const d = derived(reader => /** @description hiddenModifiedRangeStart */ r.getHiddenModifiedRange(reader).startLineNumber - 1); const modViewZone = new PlaceholderViewZone(d, 24); modViewZones.push(modViewZone); - store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, r.modifiedRange, false, modifiedOutlineSource)); + store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, r.modifiedRange, false, modifiedOutlineSource, l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined))); } } @@ -236,13 +236,13 @@ class OutlineSource extends Disposable { })); } - public getBreadcrumbItems(startRange: LineRange, reader: IReader): { name: string; kind: SymbolKind }[] { + public getBreadcrumbItems(startRange: LineRange, reader: IReader): { name: string; kind: SymbolKind; startLineNumber: number }[] { const m = this._currentModel.read(reader); if (!m) { return []; } const symbols = m.asListOfDocumentSymbols() .filter(s => startRange.contains(s.range.startLineNumber) && !startRange.contains(s.range.endLineNumber)); symbols.sort(reverseOrder(compareBy(s => s.range.endLineNumber - s.range.startLineNumber, numberComparator))); - return symbols.map(s => ({ name: s.name, kind: s.kind })); + return symbols.map(s => ({ name: s.name, kind: s.kind, startLineNumber: s.range.startLineNumber })); } } @@ -265,6 +265,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { private readonly _unchangedRegionRange: LineRange, private readonly hide: boolean, private readonly _modifiedOutlineSource: OutlineSource, + private readonly _revealHiddenLine: (lineNumber: number) => void, ) { const root = h('div.diff-hidden-lines-widget'); super(_editor, _viewZone, root.root); @@ -370,17 +371,26 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { const items = this._modifiedOutlineSource.getBreadcrumbItems(range, reader); if (items.length > 0) { - children.push($('span', undefined, '\u00a0|\u00a0')); - - let isFirst = true; - for (const item of items) { - if (!isFirst) { - children.push($('span', {}, ' ', renderIcon(Codicon.chevronRight), ' ')); - } + children.push($('span', undefined, '\u00a0\u00a0|\u00a0\u00a0')); + for (let i = 0; i < items.length; i++) { + const item = items[i]; const icon = SymbolKinds.toIcon(item.kind); - children.push($('span', {}, renderIcon(icon), ' ', item.name)); - isFirst = false; + const divItem = h('div.breadcrumb-item', { + style: { display: 'flex', alignItems: 'center' }, + }, [ + renderIcon(icon), + '\u00a0', + item.name, + ...(i === items.length - 1 + ? [] + : [renderIcon(Codicon.chevronRight)] + ) + ]).root; + children.push(divItem); + divItem.onclick = () => { + this._revealHiddenLine(item.startLineNumber); + }; } } } From 7ad4d0ca2f0153b1e445f046da540ad22cc73ad8 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 28 Aug 2023 12:15:31 +0200 Subject: [PATCH 254/607] fix https://github.com/microsoft/vscode/issues/191325 (#191448) --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 9211cd3decf..22fa7481b12 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -170,6 +170,7 @@ export class TitlebarPart extends Part implements ITitleService { if (event.affectsConfiguration(TitlebarPart.configCommandCenter)) { this.updateTitle(); this._onDidChangeCommandCenterVisibility.fire(); + this._onDidChange.fire(undefined); } } From 89b1e8b3f8b87da2d1c517b37ba9769e9a648aba Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 28 Aug 2023 12:21:05 +0200 Subject: [PATCH 255/607] fix #191174 (#191316) * fix #191174 * fix tests --- .../contrib/extensions/browser/extensionsActions.ts | 1 - .../test/electron-sandbox/extensionsActions.test.ts | 12 ++++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index fd41ea73684..6bfd77d60b4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1121,7 +1121,6 @@ export class ManageExtensionAction extends ExtensionDropDownAction { const state = this.extension.state; this.enabled = state === ExtensionState.Installed; this.class = this.enabled || state === ExtensionState.Uninstalling ? ManageExtensionAction.Class : ManageExtensionAction.HideManageExtensionClass; - this.tooltip = state === ExtensionState.Uninstalling ? localize('ManageExtensionAction.uninstallingTooltip', "Uninstalling") : ''; } } } diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts index afbbf237cf9..6a1743a35f9 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts @@ -447,7 +447,7 @@ suite('ExtensionsActions', () => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage', testObject.class); - assert.strictEqual('', testObject.tooltip); + assert.strictEqual('Manage', testObject.tooltip); }); }); @@ -462,7 +462,7 @@ suite('ExtensionsActions', () => { testObject.extension = page.firstPage[0]; assert.ok(!testObject.enabled); assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage hide', testObject.class); - assert.strictEqual('', testObject.tooltip); + assert.strictEqual('Manage', testObject.tooltip); }); }); @@ -479,7 +479,7 @@ suite('ExtensionsActions', () => { installEvent.fire({ identifier: gallery.identifier, source: gallery }); assert.ok(!testObject.enabled); assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage hide', testObject.class); - assert.strictEqual('', testObject.tooltip); + assert.strictEqual('Manage', testObject.tooltip); }); }); @@ -498,7 +498,7 @@ suite('ExtensionsActions', () => { await promise; assert.ok(testObject.enabled); assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage', testObject.class); - assert.strictEqual('', testObject.tooltip); + assert.strictEqual('Manage', testObject.tooltip); }); test('Test ManageExtensionAction when extension is system extension', () => { @@ -512,7 +512,7 @@ suite('ExtensionsActions', () => { testObject.extension = extensions[0]; assert.ok(testObject.enabled); assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage', testObject.class); - assert.strictEqual('', testObject.tooltip); + assert.strictEqual('Manage', testObject.tooltip); }); }); @@ -529,7 +529,7 @@ suite('ExtensionsActions', () => { assert.ok(!testObject.enabled); assert.strictEqual('extension-action icon manage codicon codicon-extensions-manage', testObject.class); - assert.strictEqual('Uninstalling', testObject.tooltip); + assert.strictEqual('Manage', testObject.tooltip); }); }); From a357413f4916bb5a40aa97ddaf91ca3b8fb0e0a1 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 28 Aug 2023 04:00:52 -0700 Subject: [PATCH 256/607] Fix external link providers Fixes #191032 --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index e02fbf99e5e..28b600107aa 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -170,7 +170,7 @@ export interface IDetachedTerminalInstance extends IDisposable { attachToElement(container: HTMLElement, options?: Partial): void; } -export const isDetachedTerminalInstance = (t: ITerminalInstance | IDetachedTerminalInstance): t is IDetachedTerminalInstance => typeof (t as ITerminalInstance).instanceId === 'number'; +export const isDetachedTerminalInstance = (t: ITerminalInstance | IDetachedTerminalInstance): t is IDetachedTerminalInstance => typeof (t as ITerminalInstance).instanceId !== 'number'; export interface ITerminalService extends ITerminalInstanceHost { readonly _serviceBrand: undefined; From 74a11f3d2b92d75e0737754993338cba1e85c1ee Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 28 Aug 2023 14:25:26 +0200 Subject: [PATCH 257/607] adding some working code, use classes instead --- .../lib/stylelint/vscode-known-variables.json | 3 ++- .../stickyScroll/browser/stickyScroll.css | 1 + .../browser/stickyScrollWidget.ts | 24 +++++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index a7d22a17b85..fcace4c66d4 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -757,6 +757,7 @@ "--vscode-sash-hover-size", "--vscode-sash-size", "--vscode-editorStickyScroll-scrollableWidth", + "--vscode-editorStickyScroll-foldingTransition", "--window-border-color", "--workspace-trust-check-color", "--workspace-trust-selected-color", @@ -779,4 +780,4 @@ "--z-index-notebook-sticky-scroll", "--zoom-factor" ] -} \ No newline at end of file +} diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 212fbb7a050..2904d04f603 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -35,6 +35,7 @@ .monaco-editor .sticky-line-number .codicon { float: right; + transition: var(--vscode-editorStickyScroll-foldingTransition); } .monaco-editor .sticky-line-content { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index e4d843f0551..493faf9fde5 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -191,6 +191,15 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._isOnFoldingGlyphMargin = true; + for (const line of this._stickyLines) { + const foldingIcon = line.foldingIcon; + if (!foldingIcon) { + continue; + } + foldingIcon.setVisible(true); + } + + /* for (const line of this._stickyLines) { const foldingIcon = line.foldingIcon; if (!foldingIcon) { @@ -200,6 +209,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { foldingIcon.setVisible(true); foldingIcon.setTransitionRequired(false); } + */ })); this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_LEAVE, () => { @@ -213,6 +223,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { foldingIcon.setTransitionRequired(true); foldingIcon?.setVisible(foldingIcon.isCollapsed); } + })); } @@ -320,8 +331,17 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const isCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); const foldingIcon = new StickyFoldingIcon(isCollapsed, this._lineHeight); container.append(foldingIcon.domNode); - foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); - foldingIcon.setTransitionRequired(true); + + // If we are on the glyph margin + console.log('this._isOnFoldingGlyphMargin : ', this._isOnFoldingGlyphMargin); + + if (this._isOnFoldingGlyphMargin) { + foldingIcon.setTransitionRequired(false); + foldingIcon.setVisible(true); + } else { + foldingIcon.setTransitionRequired(true); + foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); + } this._foldingIconStore.add(dom.addDisposableListener(foldingIcon.domNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); From 14c1d7c061c6e78e2dae575ff64f29d3374b0298 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 28 Aug 2023 15:04:43 +0200 Subject: [PATCH 258/607] adding code changes --- .../lib/stylelint/vscode-known-variables.json | 2 +- .../stickyScroll/browser/stickyScroll.css | 2 +- .../browser/stickyScrollWidget.ts | 71 ++++++------------- 3 files changed, 24 insertions(+), 51 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index fcace4c66d4..1fb81747019 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -757,7 +757,7 @@ "--vscode-sash-hover-size", "--vscode-sash-size", "--vscode-editorStickyScroll-scrollableWidth", - "--vscode-editorStickyScroll-foldingTransition", + "--vscode-editorStickyScroll-foldingOpacityTransition", "--window-border-color", "--workspace-trust-check-color", "--workspace-trust-selected-color", diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 2904d04f603..b446742a2db 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -35,7 +35,7 @@ .monaco-editor .sticky-line-number .codicon { float: right; - transition: var(--vscode-editorStickyScroll-foldingTransition); + transition: var(--vscode-editorStickyScroll-foldingOpacityTransition); } .monaco-editor .sticky-line-content { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 493faf9fde5..f98f67e3570 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -45,7 +45,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _lineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; private _minContentWidthInPx: number = 0; - private _isOnFoldingGlyphMargin: boolean = false; + private _isOnGlyphMargin: boolean = false; constructor( private readonly _editor: ICodeEditor @@ -148,6 +148,20 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._rootDomNode.style.display = 'none'; } + private _useFoldingOpacityTransition(requireTransitions: boolean) { + this._lineNumbersDomNode.style.setProperty('--vscode-editorStickyScroll-foldingOpacityTransition', `opacity ${requireTransitions ? 0.5 : 0}s`); + } + + private _setFoldingIconsVisibility(allVisible: boolean) { + for (const line of this._stickyLines) { + const foldingIcon = line.foldingIcon; + if (!foldingIcon) { + continue; + } + foldingIcon.setVisible(allVisible ? true : foldingIcon.isCollapsed); + } + } + private async _renderRootNode(): Promise { const foldingModel = await FoldingController.get(this._editor)?.getFoldingModel(); @@ -160,6 +174,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } if (foldingModel) { this._setFoldingHoverListeners(); + this._useFoldingOpacityTransition(!this._isOnGlyphMargin); } const widgetHeight: number = this._lineNumbers.length * this._lineHeight + this._lastLineRelativePosition; @@ -188,41 +203,13 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { return; } this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_ENTER, (e) => { - - this._isOnFoldingGlyphMargin = true; - - for (const line of this._stickyLines) { - const foldingIcon = line.foldingIcon; - if (!foldingIcon) { - continue; - } - foldingIcon.setVisible(true); - } - - /* - for (const line of this._stickyLines) { - const foldingIcon = line.foldingIcon; - if (!foldingIcon) { - continue; - } - foldingIcon.setTransitionRequired(true); - foldingIcon.setVisible(true); - foldingIcon.setTransitionRequired(false); - } - */ + this._isOnGlyphMargin = true; + this._setFoldingIconsVisibility(true); })); this._foldingIconStore.add(dom.addDisposableListener(this._lineNumbersDomNode, dom.EventType.MOUSE_LEAVE, () => { - - this._isOnFoldingGlyphMargin = false; - - for (const line of this._stickyLines) { - const foldingIcon = line.foldingIcon; - if (!foldingIcon) { - continue; - } - foldingIcon.setTransitionRequired(true); - foldingIcon?.setVisible(foldingIcon.isCollapsed); - } + this._isOnGlyphMargin = false; + this._useFoldingOpacityTransition(true); + this._setFoldingIconsVisibility(false); })); } @@ -331,17 +318,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const isCollapsed = foldingRegions.isCollapsed(indexOfFoldingRegion); const foldingIcon = new StickyFoldingIcon(isCollapsed, this._lineHeight); container.append(foldingIcon.domNode); - - // If we are on the glyph margin - console.log('this._isOnFoldingGlyphMargin : ', this._isOnFoldingGlyphMargin); - - if (this._isOnFoldingGlyphMargin) { - foldingIcon.setTransitionRequired(false); - foldingIcon.setVisible(true); - } else { - foldingIcon.setTransitionRequired(true); - foldingIcon.setVisible(isCollapsed || showFoldingControls === 'always'); - } + foldingIcon.setVisible(this._isOnGlyphMargin ? true : (isCollapsed || showFoldingControls === 'always')); this._foldingIconStore.add(dom.addDisposableListener(foldingIcon.domNode, dom.EventType.CLICK, () => { toggleCollapseState(foldingModel, Number.MAX_VALUE, [line]); @@ -462,8 +439,4 @@ class StickyFoldingIcon { this.domNode.style.cursor = visible ? 'pointer' : 'default'; this.domNode.style.opacity = visible ? '1' : '0'; } - - public setTransitionRequired(transitionRequired: boolean) { - this.domNode.style.transition = `opacity ${transitionRequired ? 0.5 : 0}s`; - } } From 76ef8f7cf9e64425ec92d3acfbef457695011731 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 28 Aug 2023 15:48:37 +0200 Subject: [PATCH 259/607] voice - some :lipstick: (#191459) * voice - some :lipstick: * voice - cannot `export` from worklet --- .../voiceTranscriptionWorklet.ts | 12 ++++++++---- .../workbenchVoiceRecognitionService.ts | 18 +++++++++++++++--- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts index 47c7baf3433..fc3acd9eb6a 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/voiceTranscriptionWorklet.ts @@ -10,9 +10,13 @@ declare class AudioWorkletProcessor { process(inputs: [Float32Array[]], outputs: [Float32Array[]]): boolean; } -class VoiceTranscriptionWorklet extends AudioWorkletProcessor { +interface IVoiceTranscriptionWorkletOptions extends AudioWorkletNodeOptions { + processorOptions: { + readonly bufferTimespan: number; + }; +} - private static readonly BUFFER_TIMESPAN = 1000; +class VoiceTranscriptionWorklet extends AudioWorkletProcessor { private startTime: number | undefined = undefined; private stopped: boolean = false; @@ -21,7 +25,7 @@ class VoiceTranscriptionWorklet extends AudioWorkletProcessor { private sharedProcessConnection: MessagePort | undefined = undefined; - constructor() { + constructor(private readonly options: IVoiceTranscriptionWorkletOptions) { super(); this.registerListeners(); @@ -71,7 +75,7 @@ class VoiceTranscriptionWorklet extends AudioWorkletProcessor { this.buffer.push(inputChannelData.slice(0)); - if (Date.now() - this.startTime > VoiceTranscriptionWorklet.BUFFER_TIMESPAN && this.sharedProcessConnection) { + if (Date.now() - this.startTime > this.options.processorOptions.bufferTimespan && this.sharedProcessConnection) { const buffer = this.buffer; this.buffer = []; diff --git a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts index d35dd681f77..11aa0dae3e1 100644 --- a/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts +++ b/src/vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService.ts @@ -38,11 +38,17 @@ export interface IWorkbenchVoiceRecognitionService { transcribe(cancellation: CancellationToken, options?: IWorkbenchVoiceRecognitionOptions): Promise>; } +interface IVoiceTranscriptionWorkletOptions extends AudioWorkletNodeOptions { + processorOptions: { + readonly bufferTimespan: number; + }; +} + class VoiceTranscriptionWorkletNode extends AudioWorkletNode { constructor( context: BaseAudioContext, - options: AudioWorkletNodeOptions, + options: IVoiceTranscriptionWorkletOptions, private readonly onDidTranscribe: Emitter, private readonly sharedProcessService: ISharedProcessService ) { @@ -86,6 +92,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit private static readonly AUDIO_BIT_DEPTH = 16; private static readonly AUDIO_CHANNELS = 1; + private static readonly BUFFER_TIMESPAN = 1000; + constructor( @IProgressService private readonly progressService: IProgressService, @ISharedProcessService private readonly sharedProcessService: ISharedProcessService, @@ -125,7 +133,8 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit sampleSize: WorkbenchVoiceRecognitionService.AUDIO_BIT_DEPTH, channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, autoGainControl: true, - noiseSuppression: true + noiseSuppression: true, + echoCancellation: false } }); @@ -161,7 +170,10 @@ export class WorkbenchVoiceRecognitionService implements IWorkbenchVoiceRecognit const voiceTranscriptionTarget = new VoiceTranscriptionWorkletNode(audioContext, { channelCount: WorkbenchVoiceRecognitionService.AUDIO_CHANNELS, - channelCountMode: 'explicit' + channelCountMode: 'explicit', + processorOptions: { + bufferTimespan: WorkbenchVoiceRecognitionService.BUFFER_TIMESPAN + } }, onDidTranscribe, this.sharedProcessService); await voiceTranscriptionTarget.start(cts.token); From 4b37efe37504412948604efcd0b9d23988fa5159 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 28 Aug 2023 13:26:18 +0200 Subject: [PATCH 260/607] August 2023 endgame OSS Tool changes --- ThirdPartyNotices.txt | 1291 ++++++++++++++++++++----------------- cli/ThirdPartyNotices.txt | 121 ++-- package.json | 2 +- 3 files changed, 757 insertions(+), 657 deletions(-) diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index cf52ca35c94..6863bf7828f 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -440,580 +440,6 @@ Title to copyright in this work will at all times remain with copyright holders. --------------------------------------------------------- -dompurify 2.3.1 - Apache 2.0 -https://github.com/cure53/DOMPurify - -DOMPurify -Copyright 2023 Dr.-Ing. Mario Heiderich, Cure53 - -DOMPurify is free software; you can redistribute it and/or modify it under the -terms of either: - -a) the Apache License Version 2.0, or -b) the Mozilla Public License Version 2.0 - ------------------------------------------------------------------------------ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - ------------------------------------------------------------------------------ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. "Contributor" - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. "Contributor Version" - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. "Contribution" - - means Covered Software of a particular Contributor. - -1.4. "Covered Software" - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. "Incompatible With Secondary Licenses" - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. "Executable Form" - - means any form of the work other than Source Code Form. - -1.7. "Larger Work" - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. "License" - - means this document. - -1.9. "Licensable" - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. "Modifications" - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. "Patent Claims" of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. "Secondary License" - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. "Source Code Form" - - means the form of the work preferred for making modifications. - -1.14. "You" (or "Your") - - means an individual or a legal entity exercising rights under this - License. For legal entities, "You" includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, "control" means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an "as is" basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - "Incompatible With Secondary Licenses" Notice - - This Source Code Form is "Incompatible - With Secondary Licenses", as defined by - the Mozilla Public License, v. 2.0. ---------------------------------------------------------- - ---------------------------------------------------------- - dotnet/csharp-tmLanguage 0.1.0 - MIT https://github.com/dotnet/csharp-tmLanguage @@ -1345,27 +771,682 @@ SOFTWARE. jeff-hykin/better-cpp-syntax 1.17.4 - MIT https://github.com/jeff-hykin/better-cpp-syntax -MIT License +The MIT License (MIT) -Copyright (c) 2019 Jeff Hykin +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + Preamble -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. --------------------------------------------------------- --------------------------------------------------------- @@ -1454,6 +1535,34 @@ SOFTWARE. --------------------------------------------------------- +jeff-hykin/better-snippet-syntax 1.0.2 - MIT +https://github.com/jeff-hykin/better-snippet-syntax + +MIT License + +Copyright (c) 2019 Jeff Hykin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + jlelong/vscode-latex-basics 1.5.3 - MIT https://github.com/jlelong/vscode-latex-basics diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index ba85a27096c..09856f72402 100644 --- a/cli/ThirdPartyNotices.txt +++ b/cli/ThirdPartyNotices.txt @@ -525,35 +525,6 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -atty 0.2.14 - MIT -https://github.com/softprops/atty - -The MIT License (MIT) - -Copyright (c) 2015-2019 Doug Tangren - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------- - ---------------------------------------------------------- - autocfg 1.1.0 - Apache-2.0 OR MIT https://github.com/cuviper/autocfg @@ -1875,7 +1846,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -crossbeam-utils 0.8.12 - MIT OR Apache-2.0 +crossbeam-utils 0.8.16 - MIT OR Apache-2.0 https://github.com/crossbeam-rs/crossbeam The MIT License (MIT) @@ -1965,7 +1936,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted --------------------------------------------------------- -cxx 1.0.78 - MIT OR Apache-2.0 +cxx 1.0.97 - MIT OR Apache-2.0 https://github.com/dtolnay/cxx Permission is hereby granted, free of charge, to any @@ -1995,7 +1966,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -cxx-build 1.0.78 - MIT OR Apache-2.0 +cxx-build 1.0.97 - MIT OR Apache-2.0 https://github.com/dtolnay/cxx Permission is hereby granted, free of charge, to any @@ -2025,7 +1996,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -cxxbridge-flags 1.0.78 - MIT OR Apache-2.0 +cxxbridge-flags 1.0.97 - MIT OR Apache-2.0 https://github.com/dtolnay/cxx Permission is hereby granted, free of charge, to any @@ -2055,7 +2026,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -cxxbridge-macro 1.0.78 - MIT OR Apache-2.0 +cxxbridge-macro 1.0.97 - MIT OR Apache-2.0 https://github.com/dtolnay/cxx Permission is hereby granted, free of charge, to any @@ -3553,7 +3524,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted --------------------------------------------------------- -http 0.2.8 - MIT OR Apache-2.0 +http 0.2.9 - MIT OR Apache-2.0 https://github.com/hyperium/http Copyright (c) 2017 http-rs authors @@ -3725,7 +3696,7 @@ THE SOFTWARE. --------------------------------------------------------- -iana-time-zone 0.1.51 - MIT OR Apache-2.0 +iana-time-zone 0.1.57 - MIT OR Apache-2.0 https://github.com/strawlab/iana-time-zone Copyright (c) 2020 Andrew D. Straw @@ -3757,7 +3728,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -iana-time-zone-haiku 0.1.0 - MIT OR Apache-2.0 +iana-time-zone-haiku 0.1.1 - MIT OR Apache-2.0 https://github.com/strawlab/iana-time-zone Copyright (c) 2020 Andrew D. Straw @@ -4259,7 +4230,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -link-cplusplus 1.0.7 - MIT OR Apache-2.0 +link-cplusplus 1.0.9 - MIT OR Apache-2.0 https://github.com/dtolnay/link-cplusplus Permission is hereby granted, free of charge, to any @@ -7734,7 +7705,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -scratch 1.0.2 - MIT OR Apache-2.0 +scratch 1.0.7 - MIT OR Apache-2.0 https://github.com/dtolnay/scratch Permission is hereby granted, free of charge, to any @@ -8753,7 +8724,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -termcolor 1.1.3 - Unlicense OR MIT +termcolor 1.2.0 - Unlicense OR MIT https://github.com/BurntSushi/termcolor The MIT License (MIT) @@ -8841,7 +8812,6 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -time 0.1.44 - MIT/Apache-2.0 time 0.3.21 - MIT OR Apache-2.0 https://github.com/time-rs/time @@ -9136,31 +9106,25 @@ DEALINGS IN THE SOFTWARE. toml 0.5.9 - MIT/Apache-2.0 https://github.com/toml-rs/toml -Copyright (c) 2014 Alex Crichton +Copyright (c) Individual contributors -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. --------------------------------------------------------- --------------------------------------------------------- @@ -9357,7 +9321,7 @@ THE SOFTWARE. --------------------------------------------------------- -tunnels 2621784a9ad72aa39500372391332a14bad581a3 +tunnels 3141ad7be00e18c4231f7c4fb6c11f9219ac49af https://github.com/microsoft/dev-tunnels MIT License @@ -9852,7 +9816,6 @@ THE SOFTWARE. --------------------------------------------------------- -wasi 0.10.0+wasi-snapshot-preview1 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT wasi 0.11.0+wasi-snapshot-preview1 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT wasi 0.9.0+wasi-snapshot-preview1 - Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT https://github.com/bytecodealliance/wasi @@ -10272,6 +10235,34 @@ SOFTWARE. --------------------------------------------------------- +windows 0.48.0 - MIT OR Apache-2.0 +https://github.com/microsoft/windows-rs + +MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE +--------------------------------------------------------- + +--------------------------------------------------------- + windows-sys 0.36.1 - MIT OR Apache-2.0 windows-sys 0.45.0 - MIT OR Apache-2.0 windows-sys 0.48.0 - MIT OR Apache-2.0 diff --git a/package.json b/package.json index 080af3cecd2..4dbaa275413 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "56bfb9ce4a8d2298f475a1b8d9c7a7b5a72204f2", + "distro": "49cc0fbc0a8e222bcce2a7c3bf62e0d23f20d258", "author": { "name": "Microsoft Corporation" }, From 8514185e28b3395660bb1f9f360fa49ba4ada375 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 28 Aug 2023 16:26:34 +0200 Subject: [PATCH 261/607] Fixes CI --- src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts | 3 ++- src/vs/editor/standalone/browser/standaloneCodeEditor.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index dc5dd6c299c..d9785bfc8c1 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -134,8 +134,9 @@ export class EmbeddedDiffEditorWidget2 extends DiffEditorWidget2 { @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, @IAudioCueService audioCueService: IAudioCueService, + @IEditorProgressService editorProgressService: IEditorProgressService, ) { - super(domElement, parentEditor.getRawOptions(), codeEditorWidgetOptions, contextKeyService, instantiationService, codeEditorService, audioCueService); + super(domElement, parentEditor.getRawOptions(), codeEditorWidgetOptions, contextKeyService, instantiationService, codeEditorService, audioCueService, editorProgressService); this._parentEditor = parentEditor; this._overwriteOptions = options; diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index d9a8d83cf09..b6cff679cf2 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -575,6 +575,7 @@ export class StandaloneDiffEditor2 extends DiffEditorWidget2 implements IStandal @IEditorProgressService editorProgressService: IEditorProgressService, @IClipboardService clipboardService: IClipboardService, @IAudioCueService audioCueService: IAudioCueService, + @IEditorProgressService editorProgressService: IEditorProgressService, ) { const options = { ..._options }; updateConfigurationService(configurationService, options, true); @@ -594,6 +595,7 @@ export class StandaloneDiffEditor2 extends DiffEditorWidget2 implements IStandal instantiationService, codeEditorService, audioCueService, + editorProgressService, ); this._configurationService = configurationService; From c7d46b2430ad5b57187df77f79a2b923fc665987 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 28 Aug 2023 16:34:46 +0200 Subject: [PATCH 262/607] Git - improve getRepositoryExact() error handling (#191462) * Git - improve getRepositoryExact() error handling * Run async operations in parallel --- extensions/git/src/git.ts | 9 ++++++-- extensions/git/src/model.ts | 38 ++++++++++++++++++++++++-------- extensions/git/src/repository.ts | 4 ++++ 3 files changed, 40 insertions(+), 11 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 62bdf24ebcb..65d34af1e31 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -397,8 +397,8 @@ export class Git { return Versions.compare(Versions.fromString(this.version), Versions.fromString(version)); } - open(repository: string, dotGit: { path: string; commonPath?: string }, logger: LogOutputChannel): Repository { - return new Repository(this, repository, dotGit, logger); + open(repositoryRoot: string, repositoryRootRealPath: string | undefined, dotGit: { path: string; commonPath?: string }, logger: LogOutputChannel): Repository { + return new Repository(this, repositoryRoot, repositoryRootRealPath, dotGit, logger); } async init(repository: string, options: InitOptions = {}): Promise { @@ -956,6 +956,7 @@ export class Repository { constructor( private _git: Git, private repositoryRoot: string, + private repositoryRootRealPath: string | undefined, readonly dotGit: { path: string; commonPath?: string }, private logger: LogOutputChannel ) { } @@ -968,6 +969,10 @@ export class Repository { return this.repositoryRoot; } + get rootRealPath(): string | undefined { + return this.repositoryRootRealPath; + } + async exec(args: string[], options: SpawnOptions = {}): Promise> { return await this.git.exec(this.repositoryRoot, args, options); } diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index eacfca8f035..02ff0ec8f5b 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -577,8 +577,8 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu } // Open repository - const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter); + const [dotGit, repositoryRootRealPath] = await Promise.all([this.git.getRepositoryDotGit(repositoryRoot), this.getRepositoryRootRealPath(repositoryRoot)]); + const repository = new Repository(this.git.open(repositoryRoot, repositoryRootRealPath, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter); this.open(repository); this._closedRepositoriesManager.deleteRepository(repository.root); @@ -615,6 +615,16 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu } } + private async getRepositoryRootRealPath(repositoryRoot: string): Promise { + try { + const repositoryRootRealPath = await fs.promises.realpath(repositoryRoot); + return !pathEquals(repositoryRoot, repositoryRootRealPath) ? repositoryRootRealPath : undefined; + } catch (err) { + this.logger.warn(`Failed to get repository realpath for: "${repositoryRoot}". ${err}`); + return undefined; + } + } + private shouldRepositoryBeIgnored(repositoryRoot: string): boolean { const config = workspace.getConfiguration('git'); const ignoredRepos = config.get('ignoredRepositories') || []; @@ -766,15 +776,25 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu } private async getRepositoryExact(repoPath: string): Promise { - const repoPathCanonical = await fs.promises.realpath(repoPath, { encoding: 'utf8' }); + // Use the repository path + const openRepository = this.openRepositories + .find(r => pathEquals(r.repository.root, repoPath)); - for (const openRepository of this.openRepositories) { - const rootPathCanonical = await fs.promises.realpath(openRepository.repository.root, { encoding: 'utf8' }); - if (pathEquals(rootPathCanonical, repoPathCanonical)) { - return openRepository.repository; - } + if (openRepository) { + return openRepository.repository; + } + + try { + // Use the repository real path + const repoPathRealPath = await fs.promises.realpath(repoPath, { encoding: 'utf8' }); + const openRepositoryRealPath = this.openRepositories + .find(r => pathEquals(r.repository.rootRealPath ?? '', repoPathRealPath)); + + return openRepositoryRealPath?.repository; + } catch (err) { + this.logger.warn(`Failed to get repository realpath for: "${repoPath}". ${err}`); + return undefined; } - return undefined; } private getOpenRepository(repository: Repository): OpenRepository | undefined; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 77739e11ce9..c3092d37b63 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -765,6 +765,10 @@ export class Repository implements Disposable { return this.repository.root; } + get rootRealPath(): string | undefined { + return this.repository.rootRealPath; + } + get dotGit(): { path: string; commonPath?: string } { return this.repository.dotGit; } From bbf51539736704c63689754ceb3096781a320417 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 24 Aug 2023 15:56:26 +0200 Subject: [PATCH 263/607] Writes module manager to global require function to allow for external module hot reloading --- src/vs/loader.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/loader.js b/src/vs/loader.js index c2d38dfca0e..a618210a168 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -1248,6 +1248,7 @@ var AMDLoader; this._buildInfoPath = []; this._buildInfoDefineStack = []; this._buildInfoDependencies = []; + this._requireFunc.moduleManager = this; } reset() { return new ModuleManager(this._env, this._scriptLoader, this._defineFunc, this._requireFunc, this._loaderAvailableTimestamp); From f650932f1661b84fdcf85cc21d39fa805ad1dc6e Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 28 Aug 2023 16:48:59 +0200 Subject: [PATCH 264/607] Fixes CI --- src/vs/editor/standalone/browser/standaloneCodeEditor.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index b6cff679cf2..c2eb25b69e9 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -575,7 +575,6 @@ export class StandaloneDiffEditor2 extends DiffEditorWidget2 implements IStandal @IEditorProgressService editorProgressService: IEditorProgressService, @IClipboardService clipboardService: IClipboardService, @IAudioCueService audioCueService: IAudioCueService, - @IEditorProgressService editorProgressService: IEditorProgressService, ) { const options = { ..._options }; updateConfigurationService(configurationService, options, true); From bfe4c57647c354fd303a1336493b95d0206ff078 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 26 Aug 2023 16:08:26 +0200 Subject: [PATCH 265/607] Adds detectedMoves to diffEditor.computeDiff telemetry event --- .../editor/browser/widget/workerBasedDocumentDiffProvider.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts index 648ba44763c..02d3c157aa6 100644 --- a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts +++ b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts @@ -84,16 +84,19 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I this.telemetryService.publicLog2<{ timeMs: number; timedOut: boolean; + detectedMoves: number; }, { owner: 'hediet'; timeMs: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'To understand if the new diff algorithm is slower/faster than the old one' }; timedOut: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'To understand how often the new diff algorithm times out' }; + detectedMoves: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'To understand how often the new diff algorithm detects moves' }; comment: 'This event gives insight about the performance of the new diff algorithm.'; }>('diffEditor.computeDiff', { timeMs, timedOut: result?.quitEarly ?? true, + detectedMoves: options.computeMoves ? (result?.moves.length ?? 0) : -1, }); if (cancellationToken.isCancellationRequested) { From 8f65d452dbe19790d952e87ccda6a0e981f68df5 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 28 Aug 2023 08:32:40 -0700 Subject: [PATCH 266/607] Add aiGenerated tag for generated workspaces. (#191467) Update tags to include aiGenerated tag --- .../tags/electron-sandbox/workspaceTagsService.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index c53bc26175e..ad3172b3878 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -638,6 +638,21 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { return Promise.resolve(tags); } + const aiGeneratedWorkspaces = URI.joinPath(this.environmentService.workspaceStorageHome, 'aiGeneratedWorkspaces.json'); + await this.fileService.exists(aiGeneratedWorkspaces).then(async result => { + if (result) { + try { + const content = await this.fileService.readFile(aiGeneratedWorkspaces); + const workspaces = JSON.parse(content.value.toString()) as string[]; + if (workspaces.indexOf(workspace.folders[0].uri.toString()) > -1) { + tags['aiGenerated'] = true; + } + } catch (e) { + // Ignore errors when resolving file contents + } + } + }); + return this.fileService.resolveAll(folders.map(resource => ({ resource }))).then((files: IFileStatResult[]) => { const names = ([]).concat(...files.map(result => result.success ? (result.stat!.children || []) : [])).map(c => c.name); const nameSet = names.reduce((s, n) => s.add(n.toLowerCase()), new Set()); From 0555ea55be59a8b5f23cf5ad74f2f940801dc0d5 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 09:22:15 -0700 Subject: [PATCH 267/607] fix #189981 --- .../accessibility/browser/accessibleView.ts | 27 +++++++++++----- .../browser/accessibility/accessibility.css | 31 +++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 0cc34ecd49c..8b9a071a1dc 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -105,9 +105,11 @@ class AccessibleView extends Disposable { private _accessibleViewCurrentProviderId: IContextKey; get editorWidget() { return this._editorWidget; } - private _editorContainer: HTMLElement; - private _currentProvider: IAccessibleContentProvider | undefined; + private _container: HTMLElement; + private _title: HTMLElement; private readonly _toolbar: WorkbenchToolBar; + + private _currentProvider: IAccessibleContentProvider | undefined; private _currentContent: string | undefined; constructor( @@ -131,12 +133,21 @@ class AccessibleView extends Disposable { this._accessibleViewGoToSymbolSupported = accessibleViewGoToSymbolSupported.bindTo(this._contextKeyService); this._accessibleViewCurrentProviderId = accessibleViewCurrentProviderId.bindTo(this._contextKeyService); - this._editorContainer = document.createElement('div'); - this._editorContainer.classList.add('accessible-view'); + this._container = document.createElement('div'); + this._container.classList.add('accessible-view'); const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { contributions: EditorExtensionsRegistry.getEditorContributions().filter(c => c.id !== CodeActionController.ID) }; - this._toolbar = this._register(_instantiationService.createInstance(WorkbenchToolBar, this._editorContainer, { orientation: ActionsOrientation.HORIZONTAL })); + const titleBar = document.createElement('div'); + titleBar.classList.add('accessible-view-title-bar'); + this._title = document.createElement('div'); + this._title.classList.add('accessible-view-title'); + titleBar.appendChild(this._title); + const actionBar = document.createElement('div'); + actionBar.classList.add('accessible-view-action-bar'); + titleBar.appendChild(actionBar); + this._container.appendChild(titleBar); + this._toolbar = this._register(_instantiationService.createInstance(WorkbenchToolBar, actionBar, { orientation: ActionsOrientation.HORIZONTAL })); this._toolbar.context = { viewId: 'accessibleView' }; const toolbarElt = this._toolbar.getElement(); toolbarElt.tabIndex = 0; @@ -155,7 +166,7 @@ class AccessibleView extends Disposable { readOnly: true, fontFamily: 'var(--monaco-monospace-font)' }; - this._editorWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._editorContainer, editorOptions, codeEditorWidgetOptions)); + this._editorWidget = this._register(this._instantiationService.createInstance(CodeEditorWidget, this._container, editorOptions, codeEditorWidgetOptions)); this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => { if (this._currentProvider && this._accessiblityHelpIsShown.get()) { this.show(this._currentProvider); @@ -334,7 +345,6 @@ class AccessibleView extends Disposable { message += '\n'; } } - this._currentContent = message + provider.provideContent() + readMoreLink + disableHelpHint + localize('exit-tip', '\nExit this dialog via the Escape key.'); this._updateContextKeys(provider, true); @@ -348,7 +358,7 @@ class AccessibleView extends Disposable { return; } model.setLanguage(provider.options.language ?? 'markdown'); - container.appendChild(this._editorContainer); + container.appendChild(this._container); let actionsHint = ''; const verbose = this._configurationService.getValue(provider.verbositySettingKey); const hasActions = this._accessibleViewSupportsNavigation.get() || this._accessibleViewVerbosityEnabled.get() || this._accessibleViewGoToSymbolSupported.get() || this._currentProvider?.actions; @@ -356,6 +366,7 @@ class AccessibleView extends Disposable { actionsHint = localize('ariaAccessibleViewActions', "Use Shift+Tab to explore actions such as disabling this hint."); } let ariaLabel = provider.options.type === AccessibleViewType.Help ? localize('accessibility-help', "Accessibility Help") : localize('accessible-view', "Accessible View"); + this._title.textContent = ariaLabel; if (actionsHint && provider.options.type === AccessibleViewType.View) { ariaLabel = localize('accessible-view-hint', "Accessible View, {0}", actionsHint); } else if (actionsHint) { diff --git a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css index 25fcb4b03d8..f8044ced3b2 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css +++ b/src/vs/workbench/contrib/codeEditor/browser/accessibility/accessibility.css @@ -21,3 +21,34 @@ width: 100%; justify-content: flex-end; } + +.accessible-view-title-bar { + display: flex; + align-items: center; + border-top-left-radius: 5px; + border-top-right-radius: 5px; +} + +.accessible-view-title { + padding: 3px 0px; + text-align: center; + text-overflow: ellipsis; + overflow: hidden; + width: 100%; +} + +.accessible-view-action-bar { + justify-content: flex-end; + margin-right: 4px; + flex: 1; +} + +.accessible-view-action-bar > .actions-container { + justify-content: flex-end; +} + +.accessible-view-title-bar .monaco-action-bar .action-label.codicon { + background-position: center; + background-repeat: no-repeat; + padding: 2px; +} From 97f81a9dac483a1d69489264c852610b88541a92 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 09:27:59 -0700 Subject: [PATCH 268/607] fix #189984 --- .../contrib/accessibility/browser/accessibleViewActions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index 6c3034b4b05..21547678e51 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -42,7 +42,7 @@ class AccessibleViewNextAction extends Action2 { ...accessibleViewMenu, when: ContextKeyExpr.and(accessibleViewIsShown, accessibleViewSupportsNavigation), }], - icon: Codicon.chevronRight, + icon: Codicon.arrowDown, title: localize('editor.action.accessibleViewNext', "Show Next in Accessible View") }); } @@ -62,7 +62,7 @@ class AccessibleViewPreviousAction extends Action2 { primary: KeyMod.Alt | KeyCode.BracketLeft, weight: KeybindingWeight.WorkbenchContrib }, - icon: Codicon.chevronLeft, + icon: Codicon.arrowUp, menu: [ commandPalette, { From ff87edf5031e64b859796951dbf46fd9d3465e54 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 28 Aug 2023 09:29:02 -0700 Subject: [PATCH 269/607] Recommend release on Stable, pre-release otherwise (#191477) --- .../workbench/contrib/preferences/browser/settingsEditor2.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index d0666ca8be5..d751cf35bdd 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -1273,8 +1273,9 @@ export class SettingsEditor2 extends EditorPane { if (toggleData && groups.filter(g => g.extensionInfo).length) { for (const key in toggleData.settingsEditorRecommendedExtensions) { const extensionId = key; - // Always recommend prerelease for now. - const [extension] = await this.extensionGalleryService.getExtensions([{ id: extensionId, preRelease: true }], CancellationToken.None); + // Recommend prerelease if not on Stable. + const isStable = this.productService.quality === 'stable'; + const [extension] = await this.extensionGalleryService.getExtensions([{ id: extensionId, preRelease: !isStable }], CancellationToken.None); if (!extension) { continue; } From 264cd8b0803ad673ebe8a97fa523c865a4c28d78 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 09:44:38 -0700 Subject: [PATCH 270/607] fix bug --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 8b9a071a1dc..2c7b4780c06 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -506,9 +506,9 @@ class AccessibleView extends Disposable { let hint = ''; const disableKeybinding = this._keybindingService.lookupKeybinding(AccessibilityCommandId.DisableVerbosityHint, this._contextKeyService)?.getAriaLabel(); if (disableKeybinding) { - hint = localize('acessibleViewDisableHint', "Disable the aria label hint to open this ({0})", disableKeybinding); + hint = localize('acessibleViewDisableHint', "Disable the aria label hint to open this ({0}).\n", disableKeybinding); } else { - hint = localize('accessibleViewDisableHintNoKb', "Add a keybinding for the command Disable Accessible View Hint to disable this hint"); + hint = localize('accessibleViewDisableHintNoKb', "Add a keybinding for the command Disable Accessible View Hint to disable this hint.\n"); } return hint; } From e311d61ab836041a644c3e3c4000ca27f3dadeff Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 09:45:48 -0700 Subject: [PATCH 271/607] add periods --- src/vs/editor/common/standaloneStrings.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index 87a6d066017..1f9525d2024 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -10,17 +10,17 @@ export namespace AccessibilityHelpNLS { export const openingDocs = nls.localize("openingDocs", "Now opening the Accessibility documentation page."); export const readonlyDiffEditor = nls.localize("readonlyDiffEditor", "You are in a read-only pane of a diff editor."); export const editableDiffEditor = nls.localize("editableDiffEditor", "You are in a pane of a diff editor."); - export const readonlyEditor = nls.localize("readonlyEditor", "You are in a read-only code editor"); - export const editableEditor = nls.localize("editableEditor", "You are in a code editor"); + export const readonlyEditor = nls.localize("readonlyEditor", "You are in a read-only code editor."); + export const editableEditor = nls.localize("editableEditor", "You are in a code editor."); export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "To configure the application to be optimized for usage with a Screen Reader press Command+E now."); export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "To configure the application to be optimized for usage with a Screen Reader press Control+E now."); export const auto_on = nls.localize("auto_on", "The application is configured to be optimized for usage with a Screen Reader."); - export const auto_off = nls.localize("auto_off", "The application is configured to never be optimized for usage with a Screen Reader"); + export const auto_off = nls.localize("auto_off", "The application is configured to never be optimized for usage with a Screen Reader."); export const screenReaderModeEnabled = nls.localize("screenReaderModeEnabled", "Screen Reader Optimized Mode enabled."); export const screenReaderModeDisabled = nls.localize("screenReaderModeDisabled", "Screen Reader Optimized Mode disabled."); export const tabFocusModeOnMsg = nls.localize("tabFocusModeOnMsg", "Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."); export const tabFocusModeOnMsgNoKb = nls.localize("tabFocusModeOnMsgNoKb", "Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."); - export const stickScrollKb = nls.localize("stickScrollKb", "Run the command: Focus Sticky Scroll ({0}) to focus the currently nested scopes"); + export const stickScrollKb = nls.localize("stickScrollKb", "Run the command: Focus Sticky Scroll ({0}) to focus the currently nested scopes."); export const stickScrollNoKb = nls.localize("stickScrollNoKb", "Run the command: Focus Sticky Scroll to focus the currently nested scopes. It is currently not triggerable by a keybinding."); export const tabFocusModeOffMsg = nls.localize("tabFocusModeOffMsg", "Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."); export const tabFocusModeOffMsgNoKb = nls.localize("tabFocusModeOffMsgNoKb", "Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding."); From 77187a9bb864ca2e522d5727f53cebfea5a5179d Mon Sep 17 00:00:00 2001 From: Logan Ramos Date: Mon, 28 Aug 2023 09:56:39 -0700 Subject: [PATCH 272/607] Add cleaning and logging for extension error data (#191331) Add cleaning and logging for error data --- .../workbench/api/common/extHostTelemetry.ts | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTelemetry.ts b/src/vs/workbench/api/common/extHostTelemetry.ts index 0fb5ed2ce40..ea9d652f143 100644 --- a/src/vs/workbench/api/common/extHostTelemetry.ts +++ b/src/vs/workbench/api/common/extHostTelemetry.ts @@ -275,8 +275,24 @@ export class ExtHostTelemetryLogger { if (typeof eventNameOrException === 'string') { this.logEvent(eventNameOrException, data); } else { - // TODO @lramos15, implement cleaning for and logging for this case - this._sender.sendErrorData(eventNameOrException, data); + const errorData = { + name: eventNameOrException.name, + message: eventNameOrException.message, + stack: eventNameOrException.stack, + cause: eventNameOrException.cause + }; + const cleanedErrorData = cleanData(errorData, []); + // Reconstruct the error object with the cleaned data + const cleanedError = new Error(cleanedErrorData.message, { + cause: cleanedErrorData.cause + }); + cleanedError.stack = cleanedErrorData.stack; + cleanedError.name = cleanedErrorData.name; + data = this.mixInCommonPropsAndCleanData(data || {}); + if (!this._inLoggingOnlyMode) { + this._sender.sendErrorData(cleanedError, data); + } + this._logger.trace('exception', data); } } From 738234ab526f5a24cc3703b6dc3d2a94af3c7a11 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 28 Aug 2023 17:24:11 +0000 Subject: [PATCH 273/607] Update `EnvironmentVariableScope` --- src/vscode-dts/vscode.d.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index d440e2d2fc1..ec7dfa27120 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11475,12 +11475,15 @@ declare module 'vscode' { getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; } - export type EnvironmentVariableScope = { + /** + * The scope object to which the environment variable collection applies to. + */ + export interface EnvironmentVariableScope { /** * Any specific workspace folder to get collection for. */ workspaceFolder?: WorkspaceFolder; - }; + } /** * A location in the editor at which progress information can be shown. It depends on the From 7360269b7a4ff1011e8d1186c5a49f8bc1c59f98 Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Mon, 28 Aug 2023 10:37:08 -0700 Subject: [PATCH 274/607] Update src/vscode-dts/vscode.d.ts Co-authored-by: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> --- src/vscode-dts/vscode.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index ec7dfa27120..5a146bee7bb 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11476,7 +11476,7 @@ declare module 'vscode' { } /** - * The scope object to which the environment variable collection applies to. + * The scope object to which the environment variable collection applies. */ export interface EnvironmentVariableScope { /** From a7a40fe0315138dc2fdd0367fd041f4454990dbd Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 28 Aug 2023 10:49:56 -0700 Subject: [PATCH 275/607] Misc UX polishes of Quick Chat (#191483) * rounded bottom * max height of sash --- src/vs/workbench/contrib/chat/browser/chatQuick.ts | 10 +++++++--- src/vs/workbench/contrib/chat/browser/media/chat.css | 5 +++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 32b6ca797fd..76bea4666b7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -26,7 +26,7 @@ export class QuickChatService extends Disposable implements IQuickChatService { readonly onDidClose = this._onDidClose.event; private _input: IQuickWidget | undefined; - // TODO: support multiple chat providers eventually + // TODO@TylerLeonhardt: support multiple chat providers eventually private _currentChat: QuickChat | undefined; private _container: HTMLElement | undefined; @@ -119,6 +119,10 @@ export class QuickChatService extends Disposable implements IQuickChatService { } class QuickChat extends Disposable { + // TODO@TylerLeonhardt: be responsive to window size + static DEFAULT_MIN_HEIGHT = 200; + static DEFAULT_MAX_HEIGHT = 900; + private widget!: ChatWidget; private sash!: Sash; private model: ChatModel | undefined; @@ -178,7 +182,7 @@ class QuickChat extends Disposable { })); this.widget.render(parent); this.widget.setVisible(true); - this.widget.setDynamicChatTreeItemLayout(2, 900); + this.widget.setDynamicChatTreeItemLayout(2, QuickChat.DEFAULT_MAX_HEIGHT); this.updateModel(); this.sash = this._register(new Sash(parent, { getHorizontalSashTop: () => parent.offsetHeight }, { orientation: Orientation.HORIZONTAL })); this.registerListeners(parent); @@ -192,7 +196,7 @@ class QuickChat extends Disposable { this._register(this.widget.onDidChangeHeight((e) => this.sash.layout())); const width = parent.offsetWidth; this._register(this.sash.onDidChange((e) => { - if (e.currentY < 200) { + if (e.currentY < QuickChat.DEFAULT_MIN_HEIGHT || e.currentY > QuickChat.DEFAULT_MAX_HEIGHT) { return; } this.widget.layout(e.currentY, width); diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 60b78970f75..5c36c297345 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -429,6 +429,11 @@ border-radius: 2px; } +.quick-input-widget .interactive-list { + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px; +} + /* #endregion */ .interactive-response-progress-tree .monaco-tl-row:hover { From 3500c500450d4326a77dd66458d7ee92dc88814b Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 28 Aug 2023 10:55:13 -0700 Subject: [PATCH 276/607] fix #191490 --- src/vs/workbench/contrib/terminal/common/terminal.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 77e37c5b574..2c67eda18cb 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -568,6 +568,7 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TerminalCommandId.AcceptSelectedSuggestion, TerminalCommandId.HideSuggestWidget, TerminalCommandId.FocusHover, + TerminalCommandId.FocusAccessibleBuffer, AccessibilityCommandId.OpenAccessibilityHelp, 'editor.action.toggleTabFocusMode', 'notifications.hideList', From ed1a0e3b20d4f5c4a22ae4cb773ca6070ba2c6af Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 28 Aug 2023 19:45:16 +0200 Subject: [PATCH 277/607] Adds diffEditor.hideUnchangedRegions settings. Fixes #190886, fixes #190887 --- .../editor/browser/widget/diffEditorWidget.ts | 18 ++++++++- .../diffEditorWidget2/diffEditorEditors.ts | 2 +- .../diffEditorWidget2/diffEditorOptions.ts | 40 ++++++------------- .../diffEditorWidget2/diffEditorViewModel.ts | 33 ++++++++++----- .../diffEditorWidget2.contribution.ts | 6 +-- .../diffEditorWidget2/diffEditorWidget2.ts | 4 +- .../diffEditorWidget2/unchangedRanges.ts | 9 +++-- src/vs/editor/common/config/diffEditor.ts | 37 +++++++++++++++++ .../config/editorConfigurationSchema.ts | 27 +++++++++++-- src/vs/editor/common/config/editorOptions.ts | 11 +++-- .../browser/widget/diffEditorWidget2.test.ts | 8 ++++ src/vs/monaco.d.ts | 10 +++-- .../codeEditor/browser/diffEditorHelper.ts | 13 ++++++ 13 files changed, 156 insertions(+), 62 deletions(-) create mode 100644 src/vs/editor/common/config/diffEditor.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 5588ae6f5ec..62d42cee6aa 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -297,7 +297,14 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE diffAlgorithm: 'advanced', accessibilityVerbose: false, experimental: { - collapseUnchangedRegions: false, + showEmptyDecorations: false, + showMoves: false, + }, + hideUnchangedRegions: { + enabled: false, + contextLineCount: 0, + minimumLineCount: 0, + revealLineCount: 0, }, isInEmbeddedEditor: false, onlyShowAccessibleDiffViewer: false, @@ -2743,8 +2750,15 @@ function validateDiffEditorOptions(options: Readonly, defaul diffWordWrap: validateDiffWordWrap(options.diffWordWrap, defaults.diffWordWrap), diffAlgorithm: validateStringSetOption(options.diffAlgorithm, defaults.diffAlgorithm, ['legacy', 'advanced'], { 'smart': 'legacy', 'experimental': 'advanced' }), accessibilityVerbose: validateBooleanOption(options.accessibilityVerbose, defaults.accessibilityVerbose), + hideUnchangedRegions: { + enabled: false, + contextLineCount: 0, + minimumLineCount: 0, + revealLineCount: 0, + }, experimental: { - collapseUnchangedRegions: false, + showEmptyDecorations: false, + showMoves: false, }, isInEmbeddedEditor: validateBooleanOption(options.isInEmbeddedEditor, defaults.isInEmbeddedEditor), onlyShowAccessibleDiffViewer: false, diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index 642359aeae4..254ce84d2be 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -147,7 +147,7 @@ export class DiffEditorEditors extends Disposable { clonedOptions.minimap = { ...(clonedOptions.minimap || {}) }; clonedOptions.minimap.enabled = false; - if (this._options.collapseUnchangedRegions.get()) { + if (this._options.hideUnchangedRegions.get()) { clonedOptions.stickyScroll = { enabled: false }; } else { clonedOptions.stickyScroll = this._options.editorOptions.get().stickyScroll; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts index 97a4e2adaaf..ffb76330146 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts @@ -6,6 +6,7 @@ import { IObservable, ISettableObservable, derived, observableValue } from 'vs/base/common/observable'; import { Constants } from 'vs/base/common/uint'; import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; +import { diffEditorDefaultOptions } from 'vs/editor/common/config/diffEditor'; import { IDiffEditorBaseOptions, IDiffEditorOptions, IEditorOptions, ValidDiffEditorBaseOptions, clampedFloat, clampedInt, boolean as validateBooleanOption, stringSet as validateStringSetOption } from 'vs/editor/common/config/editorOptions'; export class DiffEditorOptions { @@ -37,7 +38,6 @@ export class DiffEditorOptions { }); public readonly renderIndicators = derived(reader => /** @description renderIndicators */ this._options.read(reader).renderIndicators); public readonly enableSplitViewResizing = derived(reader => /** @description enableSplitViewResizing */ this._options.read(reader).enableSplitViewResizing); - public readonly collapseUnchangedRegions = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).experimental.collapseUnchangedRegions!); public readonly splitViewDefaultRatio = derived(reader => /** @description splitViewDefaultRatio */ this._options.read(reader).splitViewDefaultRatio); public readonly ignoreTrimWhitespace = derived(reader => /** @description ignoreTrimWhitespace */ this._options.read(reader).ignoreTrimWhitespace); public readonly maxComputationTimeMs = derived(reader => /** @description maxComputationTime */ this._options.read(reader).maxComputationTime); @@ -51,6 +51,11 @@ export class DiffEditorOptions { public readonly showEmptyDecorations = derived(reader => /** @description showEmptyDecorations */ this._options.read(reader).experimental.showEmptyDecorations!); public readonly onlyShowAccessibleDiffViewer = derived(reader => /** @description onlyShowAccessibleDiffViewer */ this._options.read(reader).onlyShowAccessibleDiffViewer); + public readonly hideUnchangedRegions = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.enabled!); + public readonly hideUnchangedRegionsRevealLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.revealLineCount!); + public readonly hideUnchangedRegionsContextLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.contextLineCount!); + public readonly hideUnchangedRegionsminimumLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.minimumLineCount!); + public updateOptions(changedOptions: IDiffEditorOptions): void { const newDiffEditorOptions = validateDiffEditorOptions(changedOptions, this._options.get()); const newOptions = { ...this._options.get(), ...changedOptions, ...newDiffEditorOptions }; @@ -58,32 +63,6 @@ export class DiffEditorOptions { } } -const diffEditorDefaultOptions: ValidDiffEditorBaseOptions = { - enableSplitViewResizing: true, - splitViewDefaultRatio: 0.5, - renderSideBySide: true, - renderMarginRevertIcon: true, - maxComputationTime: 5000, - maxFileSize: 50, - ignoreTrimWhitespace: true, - renderIndicators: true, - originalEditable: false, - diffCodeLens: false, - renderOverviewRuler: true, - diffWordWrap: 'inherit', - diffAlgorithm: 'advanced', - accessibilityVerbose: false, - experimental: { - collapseUnchangedRegions: false, - showMoves: false, - showEmptyDecorations: true, - }, - isInEmbeddedEditor: false, - onlyShowAccessibleDiffViewer: false, - renderSideBySideInlineBreakpoint: 900, - useInlineViewWhenSpaceIsLimited: true, -}; - function validateDiffEditorOptions(options: Readonly, defaults: ValidDiffEditorBaseOptions): ValidDiffEditorBaseOptions { return { enableSplitViewResizing: validateBooleanOption(options.enableSplitViewResizing, defaults.enableSplitViewResizing), @@ -101,10 +80,15 @@ function validateDiffEditorOptions(options: Readonly, defaul diffAlgorithm: validateStringSetOption(options.diffAlgorithm, defaults.diffAlgorithm, ['legacy', 'advanced'], { 'smart': 'legacy', 'experimental': 'advanced' }), accessibilityVerbose: validateBooleanOption(options.accessibilityVerbose, defaults.accessibilityVerbose), experimental: { - collapseUnchangedRegions: validateBooleanOption(options.experimental?.collapseUnchangedRegions, defaults.experimental.collapseUnchangedRegions!), showMoves: validateBooleanOption(options.experimental?.showMoves, defaults.experimental.showMoves!), showEmptyDecorations: validateBooleanOption(options.experimental?.showEmptyDecorations, defaults.experimental.showEmptyDecorations!), }, + hideUnchangedRegions: { + enabled: validateBooleanOption(options.hideUnchangedRegions?.enabled ?? (options.experimental as any)?.collapseUnchangedRegions, defaults.hideUnchangedRegions.enabled!), + contextLineCount: clampedInt(options.hideUnchangedRegions?.contextLineCount, defaults.hideUnchangedRegions.contextLineCount!, 0, Constants.MAX_SAFE_SMALL_INTEGER), + minimumLineCount: clampedInt(options.hideUnchangedRegions?.minimumLineCount, defaults.hideUnchangedRegions.minimumLineCount!, 0, Constants.MAX_SAFE_SMALL_INTEGER), + revealLineCount: clampedInt(options.hideUnchangedRegions?.revealLineCount, defaults.hideUnchangedRegions.revealLineCount!, 0, Constants.MAX_SAFE_SMALL_INTEGER), + }, isInEmbeddedEditor: validateBooleanOption(options.isInEmbeddedEditor, defaults.isInEmbeddedEditor), onlyShowAccessibleDiffViewer: validateBooleanOption(options.onlyShowAccessibleDiffViewer, defaults.onlyShowAccessibleDiffViewer), renderSideBySideInlineBreakpoint: clampedInt(options.renderSideBySideInlineBreakpoint, defaults.renderSideBySideInlineBreakpoint, 0, Constants.MAX_SAFE_SMALL_INTEGER), diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 58a4694c317..aff79983e40 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -35,7 +35,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo ); public readonly unchangedRegions: IObservable = derived(r => { /** @description unchangedRegions */ - if (this._options.collapseUnchangedRegions.read(r)) { + if (this._options.hideUnchangedRegions.read(r)) { return this._unchangedRegions.read(r).regions; } else { // Reset state @@ -79,8 +79,14 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const contentChangedSignal = observableSignal('contentChangedSignal'); const debouncer = this._register(new RunOnceScheduler(() => contentChangedSignal.trigger(undefined), 200)); - const updateUnchangedRegions = (result: IDocumentDiff, tx: ITransaction) => { - const newUnchangedRegions = UnchangedRegion.fromDiffs(result.changes, model.original.getLineCount(), model.modified.getLineCount()); + const updateUnchangedRegions = (result: IDocumentDiff, tx: ITransaction, reader?: IReader) => { + const newUnchangedRegions = UnchangedRegion.fromDiffs( + result.changes, + model.original.getLineCount(), + model.modified.getLineCount(), + this._options.hideUnchangedRegionsminimumLineCount.read(reader), + this._options.hideUnchangedRegionsContextLineCount.read(reader), + ); // Transfer state from cur state const lastUnchangedRegions = this._unchangedRegions.get(); @@ -122,7 +128,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo ); }; - this._register(model.modified.onDidChangeContent((e) => { const diff = this._diff.get(); if (diff) { @@ -164,6 +169,11 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo this._register(autorunWithStore(async (reader, store) => { /** @description compute diff */ + + // So that they get recomputed when these settings change + this._options.hideUnchangedRegionsminimumLineCount.read(reader); + this._options.hideUnchangedRegionsContextLineCount.read(reader); + debouncer.cancel(); contentChangedSignal.read(reader); documentDiffProviderOptionChanged.read(reader); @@ -310,13 +320,16 @@ export class DiffMapping { } export class UnchangedRegion { - public static fromDiffs(changes: readonly LineRangeMapping[], originalLineCount: number, modifiedLineCount: number): UnchangedRegion[] { + public static fromDiffs( + changes: readonly LineRangeMapping[], + originalLineCount: number, + modifiedLineCount: number, + minHiddenLineCount: number, + minContext: number, + ): UnchangedRegion[] { const inversedMappings = LineRangeMapping.inverse(changes, originalLineCount, modifiedLineCount); const result: UnchangedRegion[] = []; - const minHiddenLineCount = 3; - const minContext = 3; - for (const mapping of inversedMappings) { let origStart = mapping.originalRange.startLineNumber; let modStart = mapping.modifiedRange.startLineNumber; @@ -325,7 +338,7 @@ export class UnchangedRegion { const atStart = origStart === 1 && modStart === 1; const atEnd = origStart + length === originalLineCount + 1 && modStart + length === modifiedLineCount + 1; - if ((atStart || atEnd) && length > minContext + minHiddenLineCount) { + if ((atStart || atEnd) && length >= minContext + minHiddenLineCount) { if (atStart && !atEnd) { length -= minContext; } @@ -335,7 +348,7 @@ export class UnchangedRegion { length -= minContext; } result.push(new UnchangedRegion(origStart, modStart, length, 0, 0)); - } else if (length > minContext * 2 + minHiddenLineCount) { + } else if (length >= minContext * 2 + minHiddenLineCount) { origStart += minContext; modStart += minContext; length -= minContext * 2; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts index abb62f83158..79ef25a3e44 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts @@ -23,7 +23,7 @@ export class ToggleCollapseUnchangedRegions extends Action2 { title: { value: localize('toggleCollapseUnchangedRegions', "Toggle Collapse Unchanged Regions"), original: 'Toggle Collapse Unchanged Regions' }, icon: Codicon.map, precondition: ContextKeyEqualsExpr.create('diffEditorVersion', 2), - toggled: ContextKeyExpr.has('config.diffEditor.experimental.collapseUnchangedRegions'), + toggled: ContextKeyExpr.has('config.diffEditor.hideUnchangedRegions.enabled'), menu: { id: MenuId.EditorTitle, order: 22, @@ -35,8 +35,8 @@ export class ToggleCollapseUnchangedRegions extends Action2 { run(accessor: ServicesAccessor, ...args: unknown[]): void { const configurationService = accessor.get(IConfigurationService); - const newValue = !configurationService.getValue('diffEditor.experimental.collapseUnchangedRegions'); - configurationService.updateValue('diffEditor.experimental.collapseUnchangedRegions', newValue); + const newValue = !configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + configurationService.updateValue('diffEditor.hideUnchangedRegions.enabled', newValue); } } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 090655ac00d..9d5047da806 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -82,7 +82,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly movedBlocksLinesPart = observableValue('MovedBlocksLinesPart', undefined); - public get collapseUnchangedRegions() { return this._options.collapseUnchangedRegions.get(); } + public get collapseUnchangedRegions() { return this._options.hideUnchangedRegions.get(); } constructor( private readonly _domElement: HTMLElement, @@ -232,7 +232,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._register(applyStyle(this.elements.overlay, { width: this._layoutInfo.map((i, r) => i.originalEditor.width + (this._options.renderSideBySide.read(r) ? 0 : i.modifiedEditor.width)), - visibility: derived(reader => /** @description visibility */(this._options.collapseUnchangedRegions.read(reader) && this._diffModel.read(reader)?.diff.read(reader)?.mappings.length === 0) + visibility: derived(reader => /** @description visibility */(this._options.hideUnchangedRegions.read(reader) && this._diffModel.read(reader)?.diff.read(reader)?.mappings.length === 0) ? 'visible' : 'hidden' ), })); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index f9a38f16b69..5b5c4ecc2a9 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -94,13 +94,13 @@ export class UnchangedRangesFeature extends Disposable { const d = derived(reader => /** @description hiddenOriginalRangeStart */ r.getHiddenOriginalRange(reader).startLineNumber - 1); const origVz = new PlaceholderViewZone(d, 24); origViewZones.push(origVz); - store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource, l => this._diffModel.get()!.ensureOriginalLineIsVisible(l, undefined))); + store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource, l => this._diffModel.get()!.ensureOriginalLineIsVisible(l, undefined), this._options)); } { const d = derived(reader => /** @description hiddenModifiedRangeStart */ r.getHiddenModifiedRange(reader).startLineNumber - 1); const modViewZone = new PlaceholderViewZone(d, 24); modViewZones.push(modViewZone); - store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, r.modifiedRange, false, modifiedOutlineSource, l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined))); + store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, r.modifiedRange, false, modifiedOutlineSource, l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined), this._options)); } } @@ -266,6 +266,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { private readonly hide: boolean, private readonly _modifiedOutlineSource: OutlineSource, private readonly _revealHiddenLine: (lineNumber: number) => void, + private readonly _options: DiffEditorOptions, ) { const root = h('div.diff-hidden-lines-widget'); super(_editor, _viewZone, root.root); @@ -307,7 +308,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { const mouseUpListener = addDisposableListener(window, 'mouseup', e => { if (!didMove) { - this._unchangedRegion.showMoreAbove(20, undefined); + this._unchangedRegion.showMoreAbove(this._options.hideUnchangedRegionsRevealLineCount.get(), undefined); } this._nodes.top.classList.toggle('dragging', false); this._nodes.root.classList.toggle('dragging', false); @@ -347,7 +348,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { if (!didMove) { const top = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); - this._unchangedRegion.showMoreBelow(20, undefined); + this._unchangedRegion.showMoreBelow(this._options.hideUnchangedRegionsRevealLineCount.get(), undefined); const top2 = editor.getTopForLineNumber(this._unchangedRegionRange.endLineNumberExclusive); editor.setScrollTop(editor.getScrollTop() + (top2 - top)); } diff --git a/src/vs/editor/common/config/diffEditor.ts b/src/vs/editor/common/config/diffEditor.ts new file mode 100644 index 00000000000..2f2bc06f2d2 --- /dev/null +++ b/src/vs/editor/common/config/diffEditor.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ValidDiffEditorBaseOptions } from 'vs/editor/common/config/editorOptions'; + +export const diffEditorDefaultOptions: ValidDiffEditorBaseOptions = { + enableSplitViewResizing: true, + splitViewDefaultRatio: 0.5, + renderSideBySide: true, + renderMarginRevertIcon: true, + maxComputationTime: 5000, + maxFileSize: 50, + ignoreTrimWhitespace: true, + renderIndicators: true, + originalEditable: false, + diffCodeLens: false, + renderOverviewRuler: true, + diffWordWrap: 'inherit', + diffAlgorithm: 'advanced', + accessibilityVerbose: false, + experimental: { + showMoves: false, + showEmptyDecorations: true, + }, + hideUnchangedRegions: { + enabled: false, + contextLineCount: 3, + minimumLineCount: 3, + revealLineCount: 20, + }, + isInEmbeddedEditor: false, + onlyShowAccessibleDiffViewer: false, + renderSideBySideInlineBreakpoint: 900, + useInlineViewWhenSpaceIsLimited: true, +}; diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index b605d1dadf3..0123d64e587 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { diffEditorDefaultOptions } from 'vs/editor/common/config/diffEditor'; import { editorOptionsRegistry } from 'vs/editor/common/config/editorOptions'; import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/core/textModelDefaults'; import * as nls from 'vs/nls'; @@ -206,17 +207,35 @@ const editorConfiguration: IConfigurationNode = { 'diffEditor.diffAlgorithm': { type: 'string', enum: ['legacy', 'advanced'], - default: 'advanced', + default: diffEditorDefaultOptions.diffAlgorithm, markdownEnumDescriptions: [ nls.localize('diffAlgorithm.legacy', "Uses the legacy diffing algorithm."), nls.localize('diffAlgorithm.advanced', "Uses the advanced diffing algorithm."), ], tags: ['experimental'], }, - 'diffEditor.experimental.collapseUnchangedRegions': { + 'diffEditor.hideUnchangedRegions.enabled': { type: 'boolean', - default: false, - markdownDescription: nls.localize('collapseUnchangedRegions', "Controls whether the diff editor shows unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + default: diffEditorDefaultOptions.hideUnchangedRegions.enabled, + markdownDescription: nls.localize('hideUnchangedRegions.enabled', "Controls whether the diff editor shows unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + }, + 'diffEditor.hideUnchangedRegions.revealLineCount': { + type: 'integer', + default: diffEditorDefaultOptions.hideUnchangedRegions.revealLineCount, + markdownDescription: nls.localize('hideUnchangedRegions.revealLineCount', "Controls how many lines are used for unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + minimum: 1, + }, + 'diffEditor.hideUnchangedRegions.minimumLineCount': { + type: 'integer', + default: diffEditorDefaultOptions.hideUnchangedRegions.minimumLineCount, + markdownDescription: nls.localize('hideUnchangedRegions.minimumLineCount', "Controls how many lines are used as a minimum for unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + minimum: 1, + }, + 'diffEditor.hideUnchangedRegions.contextLineCount': { + type: 'integer', + default: diffEditorDefaultOptions.hideUnchangedRegions.contextLineCount, + markdownDescription: nls.localize('hideUnchangedRegions.contextLineCount', "Controls how many lines are used as context when comparing unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + minimum: 1, }, 'diffEditor.experimental.showMoves': { type: 'boolean', diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index eee7baf03c6..f338f7b0817 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -825,10 +825,6 @@ export interface IDiffEditorBaseOptions { accessibilityVerbose?: boolean; experimental?: { - /** - * Defaults to false. - */ - collapseUnchangedRegions?: boolean; /** * Defaults to false. */ @@ -847,6 +843,13 @@ export interface IDiffEditorBaseOptions { * If the diff editor should only show the difference review mode. */ onlyShowAccessibleDiffViewer?: boolean; + + hideUnchangedRegions?: { + enabled?: boolean; + revealLineCount?: number; + minimumLineCount?: number; + contextLineCount?: number; + }; } /** diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts index 85c9043e060..c9980e34a08 100644 --- a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts +++ b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts @@ -19,6 +19,8 @@ suite('DiffEditorWidget2', () => { [new LineRangeMapping(new LineRange(1, 10), new LineRange(1, 10), [])], 10, 10, + 3, + 3, )), []); }); @@ -27,6 +29,8 @@ suite('DiffEditorWidget2', () => { [], 10, 10, + 3, + 3, )), [ "[1,11) - [1,11)" ]); @@ -37,6 +41,8 @@ suite('DiffEditorWidget2', () => { [new LineRangeMapping(new LineRange(50, 60), new LineRange(50, 60), [])], 100, 100, + 3, + 3, )), ([ '[1,47) - [1,47)', '[63,101) - [63,101)' @@ -48,6 +54,8 @@ suite('DiffEditorWidget2', () => { [new LineRangeMapping(new LineRange(99, 100), new LineRange(100, 100), [])], 100, 100, + 3, + 3, )), (["[1,96) - [1,96)"])); }); }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 442b460ca18..3a49dbf4aa5 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3976,10 +3976,6 @@ declare namespace monaco.editor { */ accessibilityVerbose?: boolean; experimental?: { - /** - * Defaults to false. - */ - collapseUnchangedRegions?: boolean; /** * Defaults to false. */ @@ -3995,6 +3991,12 @@ declare namespace monaco.editor { * If the diff editor should only show the difference review mode. */ onlyShowAccessibleDiffViewer?: boolean; + hideUnchangedRegions?: { + enabled?: boolean; + revealLineCount?: number; + minimumLineCount?: number; + contextLineCount?: number; + }; } /** diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index 22148696b72..f9f2bd13d28 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -23,6 +23,8 @@ import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibil import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; class DiffEditorHelperContribution extends Disposable implements IDiffEditorContribution { public static readonly ID = 'editor.contrib.diffEditorHelper'; @@ -120,3 +122,14 @@ function createScreenReaderHelp(): IDisposable { } registerDiffEditorContribution(DiffEditorHelperContribution.ID, DiffEditorHelperContribution); + +Registry.as(Extensions.ConfigurationMigration) + .registerConfigurationMigrations([{ + key: 'diffEditor.experimental.collapseUnchangedRegions', + migrateFn: (value, accessor) => { + return [ + ['diffEditor.hideUnchangedRegions.enabled', { value }], + ['diffEditor.experimental.collapseUnchangedRegions', { value: undefined }] + ]; + } + }]); From 5397203187a4c8b8b893f91a948b5984b1b3944a Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 28 Aug 2023 11:26:59 -0700 Subject: [PATCH 278/607] Sort slash commands by `yieldTo` (#191112) --- .../browser/contrib/chatInputEditorContrib.ts | 79 ++++++++++++++++++- .../contrib/chat/common/chatService.ts | 5 ++ .../vscode.proposed.interactive.d.ts | 1 + 3 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 9ef0f6b1601..8be40c833c1 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -24,7 +24,7 @@ import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; import { SlashCommandContentWidget } from 'vs/workbench/contrib/chat/browser/chatSlashCommandContentWidget'; import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workbench/contrib/chat/common/chatColors'; -import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatService, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -253,14 +253,14 @@ class SlashCommandCompletions extends Disposable { } return { - suggestions: slashCommands.map(c => { + suggestions: sortSlashCommandsByYieldTo(slashCommands).map((c, i) => { const withSlash = `/${c.command}`; return { label: withSlash, insertText: c.executeImmediately ? '' : `${withSlash} `, detail: c.detail, range: new Range(1, 1, 1, 1), - sortText: c.sortText ?? c.command, + sortText: c.sortText ?? 'a'.repeat(i + 1), kind: CompletionItemKind.Text, // The icons are disabled here anyway, command: c.executeImmediately ? { id: SubmitAction.ID, title: withSlash, arguments: [{ widget, inputValue: `${withSlash} ` }] } : undefined, }; @@ -271,6 +271,79 @@ class SlashCommandCompletions extends Disposable { } } +interface SlashCommandYieldTo { + command: string; +} + +// Adapted from https://github.com/microsoft/vscode/blob/ca2c1636f87ea4705f32345c2e348e815996e129/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts#L31-L99 +function sortSlashCommandsByYieldTo; +}>(slashCommands: readonly T[]): T[] { + function yieldsTo(yTo: SlashCommandYieldTo, other: T): boolean { + return 'command' in yTo && other.command === yTo.command; + } + + // Build list of nodes each node yields to + const yieldsToMap = new Map(); + for (const slashCommand of slashCommands) { + for (const yTo of slashCommand.yieldsTo ?? []) { + for (const other of slashCommands) { + if (other.command === slashCommand.command) { + continue; + } + + if (yieldsTo(yTo, other)) { + let arr = yieldsToMap.get(slashCommand); + if (!arr) { + arr = []; + yieldsToMap.set(slashCommand, arr); + } + arr.push(other); + } + } + } + } + + if (!yieldsToMap.size) { + return Array.from(slashCommands); + } + + // Topological sort + const visited = new Set(); + const tempStack: T[] = []; + + function visit(nodes: T[]): T[] { + if (!nodes.length) { + return []; + } + + const node = nodes[0]; + if (tempStack.includes(node)) { + console.warn(`Yield to cycle detected for ${node.command}`); + return nodes; + } + + if (visited.has(node)) { + return visit(nodes.slice(1)); + } + + let pre: T[] = []; + const yTo = yieldsToMap.get(node); + if (yTo) { + tempStack.push(node); + pre = visit(yTo); + tempStack.pop(); + } + + visited.add(node); + + return [...pre, node, ...visit(nodes.slice(1))]; + } + + return visit(Array.from(slashCommands)); +} + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(SlashCommandCompletions, LifecyclePhase.Eventually); class VariableCompletions extends Disposable { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 9039dfb5150..39fb93a4739 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -90,6 +90,11 @@ export interface ISlashCommand { * Has no effect if `shouldRepopulate` is `false`. */ followupPlaceholder?: string; + /** + * The slash command(s) that this command wants to be + * deprioritized in favor of. + */ + yieldsTo?: ReadonlyArray<{ readonly command: string }>; } export interface IChatReplyFollowup { diff --git a/src/vscode-dts/vscode.proposed.interactive.d.ts b/src/vscode-dts/vscode.proposed.interactive.d.ts index 8ec16060be1..39e1e22e191 100644 --- a/src/vscode-dts/vscode.proposed.interactive.d.ts +++ b/src/vscode-dts/vscode.proposed.interactive.d.ts @@ -159,6 +159,7 @@ declare module 'vscode' { shouldRepopulate?: boolean; followupPlaceholder?: string; executeImmediately?: boolean; + yieldTo?: ReadonlyArray<{ readonly command: string }>; } export interface InteractiveSessionReplyFollowup { From 3b4fdca0cda60b5d79a3c95abb56184b0232341f Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 11:39:57 -0700 Subject: [PATCH 279/607] fix #189776 --- src/vs/platform/terminal/common/terminal.ts | 1 + .../terminal/browser/terminalActions.ts | 18 ++++++++++++++++-- .../terminal/common/terminalConfiguration.ts | 11 +++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 235d8efdbe8..e7e9dbe82e2 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -114,6 +114,7 @@ export const enum TerminalSettingId { EnableImages = 'terminal.integrated.enableImages', SmoothScrolling = 'terminal.integrated.smoothScrolling', IgnoreBracketedPasteMode = 'terminal.integrated.ignoreBracketedPasteMode', + FocusAfterRun = 'terminal.integrated.focusAfterRun', // Debug settings that are hidden from user diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 8e58f90c8fc..62b3605a7d7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; -import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { CONTEXT_ACCESSIBILITY_MODE_ENABLED, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { Action2, registerAction2, IAction2Options } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -131,6 +131,7 @@ export class TerminalLaunchHelpAction extends Action { } } + /** * A wrapper function around registerAction2 to help make registering terminal actions more concise. * The following default options are used if undefined: @@ -206,6 +207,7 @@ export function registerActiveXtermAction( }); } + export interface ITerminalServicesCollection { service: ITerminalService; groupService: ITerminalGroupService; @@ -547,6 +549,8 @@ export function registerTerminalActions() { title: { value: localize('workbench.action.terminal.runSelectedText', "Run Selected Text In Active Terminal"), original: 'Run Selected Text In Active Terminal' }, run: async (c, accessor) => { const codeEditorService = accessor.get(ICodeEditorService); + const configurationService = accessor.get(IConfigurationService); + const accessibilityService = accessor.get(IAccessibilityService); const editor = codeEditorService.getActiveCodeEditor(); if (!editor || !editor.hasModel()) { return; @@ -560,8 +564,18 @@ export function registerTerminalActions() { const endOfLinePreference = isWindows ? EndOfLinePreference.LF : EndOfLinePreference.CRLF; text = editor.getModel().getValueInRange(selection, endOfLinePreference); } - instance.sendText(text, true, true); + await instance.sendText(text, true, true); await c.service.revealActiveTerminal(); + const focusAfterRun = configurationService.getValue(TerminalSettingId.FocusAfterRun); + const focusTerminal = focusAfterRun === 'terminal' || (focusAfterRun === 'auto' && accessibilityService.isScreenReaderOptimized()); + if (focusTerminal) { + instance.focus(true); + } else if (focusAfterRun === 'accessible-buffer') { + const contribution = instance.getContribution('terminal.accessible-buffer'); + if (contribution) { + (contribution as any).show(); + } + } } }); diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index d92eac5fe3f..07dcce63ce9 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -609,6 +609,17 @@ const terminalConfiguration: IConfigurationNode = { type: 'boolean', default: true }, + [TerminalSettingId.FocusAfterRun]: { + markdownDescription: localize('terminal.integrated.focusAfterRun', "Controls whether the terminal, accessible buffer, or neither will be focused after `Terminal: Run Selected Text In Active Terminal` has been run."), + enum: ['auto', 'terminal', 'accessible-buffer', 'none'], + default: 'auto', + markdownEnumDescriptions: [ + localize('terminal.integrated.focusAfterRun.auto', "Set to `terminal` when in screen reader optimized mode and `none` otherwise."), + localize('terminal.integrated.focusAfterRun.terminal', "Always focus the terminal."), + localize('terminal.integrated.focusAfterRun.accessible-buffer', "Always focus the accessible buffer."), + localize('terminal.integrated.focusAfterRun.none', "Keep the focus in the editor."), + ] + } } }; From bf1d725ef44f65ac38d7bee5c8167814a43e8197 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 11:40:47 -0700 Subject: [PATCH 280/607] add accessibility tag --- .../workbench/contrib/terminal/common/terminalConfiguration.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 07dcce63ce9..b997c5e3c72 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -613,6 +613,7 @@ const terminalConfiguration: IConfigurationNode = { markdownDescription: localize('terminal.integrated.focusAfterRun', "Controls whether the terminal, accessible buffer, or neither will be focused after `Terminal: Run Selected Text In Active Terminal` has been run."), enum: ['auto', 'terminal', 'accessible-buffer', 'none'], default: 'auto', + tags: ['accessibility'], markdownEnumDescriptions: [ localize('terminal.integrated.focusAfterRun.auto', "Set to `terminal` when in screen reader optimized mode and `none` otherwise."), localize('terminal.integrated.focusAfterRun.terminal', "Always focus the terminal."), From d6c486841b7bb9f39467d9d47b5dc8dc0057d99c Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Mon, 28 Aug 2023 11:41:39 -0700 Subject: [PATCH 281/607] Apply suggestions from code review --- src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 62b3605a7d7..dfcfe77d906 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -131,7 +131,6 @@ export class TerminalLaunchHelpAction extends Action { } } - /** * A wrapper function around registerAction2 to help make registering terminal actions more concise. * The following default options are used if undefined: @@ -207,7 +206,6 @@ export function registerActiveXtermAction( }); } - export interface ITerminalServicesCollection { service: ITerminalService; groupService: ITerminalGroupService; From 727cd820735d8fe6e239747b1992582178d8626c Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 28 Aug 2023 20:46:04 +0200 Subject: [PATCH 282/607] Light version is always used for `iconPath` (#191127) * Light version is always used for `iconPath` Fixes #188830 * Use ThemeService instead --- src/vs/platform/quickinput/browser/quickInput.ts | 2 -- .../quickinput/browser/quickInputController.ts | 6 ++++-- src/vs/platform/quickinput/browser/quickInputList.ts | 10 ++++++---- .../platform/quickinput/browser/quickInputService.ts | 6 +++--- .../quickinput/test/browser/quickinput.test.ts | 8 ++++---- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index c56674f2f78..173bbc88096 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -31,7 +31,6 @@ import { IInputBox, IKeyMods, IQuickInput, IQuickInputButton, IQuickInputHideEve import { QuickInputBox } from './quickInputBox'; import { QuickInputList, QuickInputListFocus } from './quickInputList'; import { getIconClass, renderQuickInputDescription } from './quickInputUtils'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; export interface IQuickInputOptions { idPrefix: string; @@ -62,7 +61,6 @@ export interface IQuickInputStyles { readonly keybindingLabel: IKeybindingLabelStyles; readonly list: IListStyles; readonly pickerGroup: { pickerGroupBorder: string | undefined; pickerGroupForeground: string | undefined }; - readonly colorScheme: ColorScheme; } export interface IQuickInputWidgetStyles { diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index f42e1ff78d1..85780828ce1 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -21,6 +21,7 @@ import { QuickInputBox } from 'vs/platform/quickinput/browser/quickInputBox'; import { QuickInputList, QuickInputListFocus } from 'vs/platform/quickinput/browser/quickInputList'; import { QuickInputUI, Writeable, IQuickInputStyles, IQuickInputOptions, QuickPick, backButton, InputBox, Visibilities, QuickWidget } from 'vs/platform/quickinput/browser/quickInput'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; const $ = dom.$; @@ -50,7 +51,8 @@ export class QuickInputController extends Disposable { private previousFocusElement?: HTMLElement; - constructor(private options: IQuickInputOptions) { + constructor(private options: IQuickInputOptions, + private readonly themeService: IThemeService) { super(); this.idPrefix = options.idPrefix; this.parentElement = options.container; @@ -145,7 +147,7 @@ export class QuickInputController extends Disposable { const description1 = dom.append(container, $('.quick-input-description')); const listId = this.idPrefix + 'list'; - const list = this._register(new QuickInputList(container, listId, this.options)); + const list = this._register(new QuickInputList(container, listId, this.options, this.themeService)); inputBox.setAttribute('aria-controls', listId); this._register(list.onDidChangeFocus(() => { inputBox.setAttribute('aria-activedescendant', list.getActiveDescendant() ?? ''); diff --git a/src/vs/platform/quickinput/browser/quickInputList.ts b/src/vs/platform/quickinput/browser/quickInputList.ts index 650219bb460..e5914bfef84 100644 --- a/src/vs/platform/quickinput/browser/quickInputList.ts +++ b/src/vs/platform/quickinput/browser/quickInputList.ts @@ -34,7 +34,8 @@ import { getIconClass } from 'vs/platform/quickinput/browser/quickInputUtils'; import { IQuickPickItem, IQuickPickItemButtonEvent, IQuickPickSeparator, IQuickPickSeparatorButtonEvent, QuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { Lazy } from 'vs/base/common/lazy'; import { URI } from 'vs/base/common/uri'; -import { ColorScheme, isDark } from 'vs/platform/theme/common/theme'; +import { isDark } from 'vs/platform/theme/common/theme'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; const $ = dom.$; @@ -234,7 +235,7 @@ class ListElementRenderer implements IListRenderer { // always prefer item over separator because if item is defined, it must be the main item type diff --git a/src/vs/platform/quickinput/browser/quickInputService.ts b/src/vs/platform/quickinput/browser/quickInputService.ts index f797cd31f05..c4d20832264 100644 --- a/src/vs/platform/quickinput/browser/quickInputService.ts +++ b/src/vs/platform/quickinput/browser/quickInputService.ts @@ -98,7 +98,8 @@ export class QuickInputService extends Themable implements IQuickInputService { const controller = this._register(new QuickInputController({ ...defaultOptions, ...options - })); + }, + this.themeService)); controller.layout(host.dimension, host.offset.quickPickTop); @@ -225,8 +226,7 @@ export class QuickInputService extends Themable implements IQuickInputService { pickerGroup: { pickerGroupBorder: asCssVariable(pickerGroupBorder), pickerGroupForeground: asCssVariable(pickerGroupForeground), - }, - colorScheme: this.themeService.getColorTheme().type + } }; } } diff --git a/src/vs/platform/quickinput/test/browser/quickinput.test.ts b/src/vs/platform/quickinput/test/browser/quickinput.test.ts index b50d6523c68..2ca1fb2b16e 100644 --- a/src/vs/platform/quickinput/test/browser/quickinput.test.ts +++ b/src/vs/platform/quickinput/test/browser/quickinput.test.ts @@ -15,7 +15,7 @@ import { unthemedKeybindingLabelOptions } from 'vs/base/browser/ui/keybindingLab import { unthemedProgressBarOptions } from 'vs/base/browser/ui/progressbar/progressbar'; import { QuickInputController } from 'vs/platform/quickinput/browser/quickInputController'; import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; // Sets up an `onShow` listener to allow us to wait until the quick pick is shown (useful when triggering an `accept()` right after launching a quick pick) // kick this off before you launch the picker and then await the promise returned after you launch the picker. @@ -82,10 +82,10 @@ suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 pickerGroup: { pickerGroupBorder: undefined, pickerGroupForeground: undefined, - }, - colorScheme: ColorScheme.DARK + } } - }); + }, + new TestThemeService()); // initial layout controller.layout({ height: 20, width: 40 }, 0); From 91c9158a5c1682b4f0e985eb901e307857c13666 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 28 Aug 2023 21:17:23 +0200 Subject: [PATCH 283/607] Uses version2 of diff editor by default. --- src/vs/editor/common/config/editorConfigurationSchema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 0123d64e587..845adcd40ac 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -244,7 +244,7 @@ const editorConfiguration: IConfigurationNode = { }, 'diffEditor.experimental.useVersion2': { type: 'boolean', - default: false, + default: true, description: nls.localize('useVersion2', "Controls whether the diff editor uses the new or the old implementation."), tags: ['experimental'], }, From 2dc082211bb9768a1e0b4b6ff1d72ae53bd78cc5 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:35:08 -0700 Subject: [PATCH 284/607] Context menu for Quick Search appearing in search view (#191509) Fixes #191485 --- .../contrib/search/browser/searchActionsTextQuickAccess.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts b/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts index 3971c1f112c..b517ae9e2b3 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsTextQuickAccess.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; import { RenderableMatch } from 'vs/workbench/contrib/search/browser/searchModel'; -import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { category } from 'vs/workbench/contrib/search/browser/searchActionsBase'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { TEXT_SEARCH_QUICK_ACCESS_PREFIX } from 'vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess'; @@ -22,11 +22,6 @@ registerAction2(class TextSearchQuickAccessAction extends Action2 { original: 'Quick Text Search (Experimental)' }, category, - menu: [{ - id: MenuId.SearchContext, - group: 'search_2', - order: 1 - }], f1: true }); From 92be1f75d967ad081c15809687163124e110a486 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 28 Aug 2023 12:43:00 -0700 Subject: [PATCH 285/607] Disable image support by default This was the cause of the black rectangle when using the DOM renderer and it appears it can also have that issue with the other renderers. Fixes #191426 --- .../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 d92eac5fe3f..8a8803cd23f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -607,7 +607,7 @@ const terminalConfiguration: IConfigurationNode = { restricted: true, markdownDescription: localize('terminal.integrated.enableImages', "Enables image support in the terminal, this will only work when {0} is enabled. Both sixel and iTerm's inline image protocol are supported on Linux and macOS, Windows support will light up automatically when ConPTY passes through the sequences. Images will currently not be restored between window reloads/reconnects.", `\`#${TerminalSettingId.GpuAcceleration}#\``), type: 'boolean', - default: true + default: false }, } }; From db135a575a4af753e11b38f9b10540a2363ec16b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 28 Aug 2023 12:51:04 -0700 Subject: [PATCH 286/607] cli: fix decompression loop stalling (#191512) Fixes #191501 It turns out this was a difference in inflate/deflate implementations between the extension/SDK and the CLI. The SDK uses Node's zlib bindings, while by default Rust's flate2 library uses a rust port of [miniz][1]. The 'logic' in the CLI was good, but miniz does not appear to flush decompressed data as nicely on SYNC'd boundaries as zlib does, which caused data to 'stall'. Telling the flate2 crate to use the native bindings fixed this. This could also be the cause of the flakiness occasionally seen on idle tunnel connections! [1]: https://github.com/richgel999/miniz --- cli/Cargo.lock | 12 ++++++++++++ cli/Cargo.toml | 4 ++-- cli/src/tunnels/socket_signal.rs | 24 +++++++++++++++++++++++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 34627da135b..1e75e0541fa 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -735,6 +735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", + "libz-sys", "miniz_oxide", ] @@ -1217,6 +1218,17 @@ version = "0.2.144" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +[[package]] +name = "libz-sys" +version = "1.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "link-cplusplus" version = "1.0.9" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 50fad68a8a4..03a6f573952 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -18,8 +18,8 @@ open = "4.1.0" reqwest = { version = "0.11.18", default-features = false, features = ["json", "stream", "native-tls"] } tokio = { version = "1.28.2", features = ["full"] } tokio-util = { version = "0.7.8", features = ["compat", "codec"] } -flate2 = "1.0.26" -zip = { version = "0.6.6", default-features = false, features = ["time", "deflate"] } +flate2 = { version = "1.0.26", default-features = false, features = ["zlib"] } +zip = { version = "0.6.6", default-features = false, features = ["time", "deflate-zlib"] } regex = "1.8.3" lazy_static = "1.4.0" sysinfo = { version = "0.29.0", default-features = false } diff --git a/cli/src/tunnels/socket_signal.rs b/cli/src/tunnels/socket_signal.rs index 2a2df6607ea..69feddade61 100644 --- a/cli/src/tunnels/socket_signal.rs +++ b/cli/src/tunnels/socket_signal.rs @@ -264,8 +264,8 @@ where #[cfg(test)] mod tests { - // Note this useful idiom: importing names from outer (for mod tests) scope. use super::*; + use base64::{engine::general_purpose, Engine as _}; #[test] fn test_round_trips_compression() { @@ -287,4 +287,26 @@ mod tests { assert_eq!(decompressed, vals); } } + + const TEST_191501_BUFS: [&'static str; 3] = [ + "TMzLSsQwFIDhfSDv0NXsYs2kubQQXIgX0IUwHVyfpCdjaSYZmkjRpxdEBnf/5vufHsZmK0PbxuwhfuRS2zmVecKVBd1rEYTUqL3gCoxBY7g2RoWOg+nE7Z4H1N3dij6nhL7OOY15wWTBeN87IVkACayTijMXcGJagevkxJ3i/e4/swFiwV1Z5ss7ukP2C9bHFc5YbF0/sXkex7eW33BK7q9maI6X0woTUvIXQ7OhK7+YkgN6dn2xF/wamhTgVM8xHl8Tr2kvvv2SymYtJZT8AAAA//8=", + "YmJAgIhqpZLKglQlK6XE0pIMJR0IZaVUlJqbX5JaXAwSSkksSQQK+WUkung5BWam6TumVaWEFhQHJBuUGrg4WUY4eQV4GOTnhwVkWJiX5lRmOdoq1QIAAAD//w==", + "jHdTdCZQk23UsW3btpOObeuLbdu2bdvs2E46tm17+p+71ty5b/ect13aVbte6n8XmfmfIv9rev8BaP8BNjYWzv8s/78S/ItxsjCzNTEW/T+s2DhZaNSE5Bi41B0kFBjZ2VjYtAzlzTWUHJWtJC2dPFUclDmZPW2EFQEAGkN3Rb7/tGPiZOFoYizy/1LhZvnXu6OZEzG3F/F/duNf6v/Zk39B9naO/yAuRi5GHx8FeWUVQob/JZTEPx9uQiZmDnrGf5/pv93+KeX0b7OEzExs/9kALo7WDBz0nEz0/wxCAICJ/T+QmoH6v0V2/udCJ2Nia+Zs/i8L47/3f+H/cOMmNLS3t7YAGP6HLIM7nZubG52pnaMN3b+kJrYAO2MT4//IGvKquY+4Oly7Z01ajWRItkE1jacYu9tcSU339/OnBkYgUbBD9rHonA9pvJV7heYuoFUpRcnKi8RwoJrSkW7ePD6N3ANHPr1UW7wPu5907dLnd4hlXwziROJkDgejfKv5ztZzPgXoUaEPEsM6y752iLyMJdkKwrSo+LAiaFp4HSRvSAnMT2Ck9JHIyQNuaFslDhaLQMIP+B7AGRyZFXeqpFF8HvfFVkQHqGejNjdizFvRHkndAl8AtfEqRHfxPFAit0twsNMyaONmusi/YHvmbQhpTRnyOV0gg+tXzisWmDsLBFAutCcGRHR0Cigere6p3A7NDGmBxHAZSmK/LGHKCeyUqN9fyBIUmyCtV99ptMaQWt4KAny5Fg+nTU1gBvBq4RvHlGCF9WL+2ZxKDfB2gr2GQaUY76Tv7x79VKbxwC5GITg2q02XPy6ZNFnLryVCGskiYPFPQLAsU+LrTvbyQTk7KNUFHwzBUTP1MiKg9LCdWAs8BZx3FHYaJyvIPw4nJpUAP3rP8GPdJeb3iIJ7i8xf15F71iT47rNv+qCXaQD9NBo8PcRVqnEy3vyrPG5SO8HwSDk9PhQJe2xo4Q52soIDB3v1jYYmR8ZkuoNq3Moy6BDjR1WBCTFJEHjdSSADxzRJ2hnozSOLmzTLuKgwWnFU1aGpQ5S8Ry7ME7gVb+CwnFvVtrpofL+DXvE3CY9Fhqe0y4Sq1yLyn/vcgA7ShFG+QnTB5zaKS3Ndj6LSCxwiNivY9R9TsAXobw4Exqog7xCAjYxNIbDuo/fC1QKpFUzvxw+7Rjc8J2lJg80YveK++I5fqJVAFu0Gb4SuJAd8ernBkpyy9lbou0enEfQMOjjucNiy+rgpU4pl+ERgt/Be+8G9l0RbeUwthLZp4ARnBHAB2mcB2o1cJIbhXnMiYStLmjwI+i+NOhBvRV8nmAVslkGdsEVU6Q3hYy/cT/QRTbEF0W58bkYPCyx93ESp7/sWkTG5i9GInCwW+zw1NIRfi2zkuz7KIzOlg33b5/R60L2tjlPtcLjZYL9qGWXwgPApKkndbDq0HhRCQYTyEZ1nC4MFi9NuasFm4t4UV4/W4L0A8YwsXH2m8Rh7hl1No5oIIlAGi5Er/amKw5mAA/Hvwbzfd4TGx66MHWA9t6NAA2WPx538griN7LCqE2315o09fNbOumI6fM1CN0AJT2FheQgaG4tdPFPn6uAeDXUDT8OkTdRFNi6Av4rwo6NnyfLnLYxBNdAhHs75bAedI5egbRrWLC48JT7aKsV+VsOmLsk0TGh6ISxI3WzskVbVFr6HGLy8jee1ZiMF0wzd/B4LvlyGIMa6HD+JBsGOH6vukgqV7ywTl6P+Wo8mTZHo12d7u09Z59eyXJcZKnqY4YzEzGUrlGzvO0Rgfgsse3RMPWJSpsETWqo5zMTtzYk9HANeoA5ubNoO/jjtLyModk/iH6XLiFD1591q+nXNb3Ve2v/aHlJQQYaytpOULvnsEYGIQH9+y3eK1Rgqgs7fxD3uzpv06A/afiToieIJpbjLhy3JZBEAmtN5UgJm6SuCbqgKJ+fDsuwMp/m0fCNVqrYORcBpKTvIWFzWF/leWJntKUis0dPrWy5x7Yu2GhqJh3GN2bT8w1uIh1haSlBmhMOzV3yNUmNcjqFV+GziNt6twoPDJ+4m7TE7hP2E9mEhiYihUDjT0X2Q4k0GIqdIl6fpoFPK0zdfRfbEkP2Ulr7fzfVqCYp9iuxtZFqBafBWLNHVjYtIn9/Z6Z3mP8DBfOYrXbMXldLjKW6rHr3w/LACe+LINkxcxQ9rxxBffepkhhj8NQ7vpyXpudfYmfPMsnai+b5VI5QMcyZly26kxMo6KGGilNYyX/hLaowV4GjIEY7kHRCNmJIBNevb1ag4w98wLWMtfyPMLn18o9cFKiJk2kjZmRBFh0S0Bd7AjxiNO8YdDQ83lBGS5JrxmLG+hW2oGYQllWS2UjK3+loONmC6NpPNgUiNhDQ05s24iRJZ/bzrgBskPLGukoMu8NK8CQNKZE8zzmsCrnkU53iPeZd/UT8ox6WMMZOtDv8YyQpTmhbzXCQW9ogbfgqH447dJFZuPkT4MGfKw+0c5L6aLWqAadBU9yLftFVsi8GZOSB9Ctv9/fJZ5SmlNgt25uGvspB9y1PQGEmLQyjFiGK7kveEw4Knn9lv/9GV2YlCdeRTAUyOS56k6G4ajfxNtMHPaDqIWTM1yBem3dShwkhD0nMXit14/wHRHosy59T+nkuvxG1MbTx8GJM45rvrOmUW0nwxNNdsdqFCNPWn+GcYzIdwCNFtHmdSKNOecfZZVJnKzuGbs41wRQIkv1E1p6ITiPxv+zKWflEU76wHOPrDx4rmyw3Z6MqaP316eOcW43JwBvp9hJuMUHr0TFkvjd5KzvmUSrZfYvpPZ2humVwOsjChiFzc7aoBMt8MdXyf2LIhuhBAg8Ue3wLqlg3cEYBS2z+uzrS5bJzmzH3NGmI+M/WbHOkbqcNtSoZjwp4NI5bSpCKWs7BqrK8sfsUC+UpA08Lfc4CpcBmsTyuHncO2gLc9jPMT+SBAgiZxTDncaiM+YG19ntqYSttys+jpASZDwEWjYRN8QURClAIs0G0KKoY0jjWcc0rypYXiCsHD9+kjtnYJHuzeZw2GQ5U5j7acLM8nyuy8bSJaKZXFq8TJkQ/p4lSkKHpVQPi+dWF4jYaQFEGiPAuiLOGzOE/f8B2rePs9zps7QivUyIiM8fsbPx5mwaC7FbjdihjbM198akLx99SpXAF4fh6d/xwLppw2kFrKa0UsTa/emTuV+6l2/8WmVWLd8JJAhcE+qbMrJBrohgGdDNZIRxJOrsFCzSmu2ykTCZnZlPITlbK/hUA/+DwdtJbmzKczEWAS9ENNbxHNSbn4Nqsz0yvhUE2a/FT6tvnBbXm/X2yLQQhxuVyNCsK2TeUNifqlsCEAJAALqqNI/NX+owJEAk+KehT/fpCsXGTsT3kFsUiPNWAkOEuHviK3Nzpu53edKRZgInWOWhGnd8aD6k7kio0tLT8i/PkxVrdZftlNrqPZfiEXkqX3hM526HzLGVzlr+CvTBKxsU8ROxHvBGWzJk4Tt0uDhZessy5BDFVx2xiYxMTXfQyv8NF0Op3CKCFvH1KbE2Z2TGCvpOEH7LKVK5TyTVSP+yah8TkpL1cHorIRxz2a5cMNMZGgdooqszII7PJuT3Ii0GpCCXe3v5mzysGhVKBulynWOeMrlJ4jKA4xzAXIg7ReLCGOntAOvU7qD+5UBufLWxx/3cqhuMcZDnR2dUjJuFG5LuFiwnvboFRMjVTvVJkcNdUc7b+0auIQWC1E3hTQx422OCMuGvayP3WMCGe8IClwSw4f1uA5LkoDYZbVQo1SUzETYNPQUK5BTJy7YRq4ln9vLvDHDImNd3TiWnsL7Zp9qWVSSTfSVSyZTT4fJqKIZ/Kcy7IkXFyv0Frw64R7y0vM+tAu+0kebn9y+DlN2xmi7nmf81iI1xffS5+ehMzQJTIa8SjVc8kCf14eOLiR7TgCnHcJieDFQI9r9K9co2G0hpitdihrbb56XvossnHl8Fu4JRLBPgKXsAQyX3v3BUHuw42rmeQXz74oZzmEIG13oteilg9HOUyoR5NHE94cYtIqP80qheAh9uQA9e3+TSmiLy6dsU625mYOYcPixVm9ZYuiOtLWQ3tT8j2T111qqjqNu6yUSxlIAh0+ANUEhEh9Uoj9v89/WqlGXNWPDmKfRtn+yFVoyggl8PjW0GB7qfreaEuoqouCGoV+lWma6sNZyKYQGIn51nzIyO1uUlRQZq5j8aTQgcXlNYi5rXALJ2Kj8nEbJT8OqXEt0fbWPKaLQZch23yR9RLyaXMpTIzzRBkoFY5g0MfTWFLbcMynydkZITcfLTSDeD/fxSqUzWmgjk9j1aQ07KUBInTRErSbfEhgCVikEENWXpOubo3XV4YBv9CJYSuXnSv0d3jLQdHefqwT7+Gyqy0ZJYicFYw3ma+acapIZw2r4qg4BNKbSbkMKOuWidsr1dxjS9bjSYoNH/VDBdbgXpXTpPJosDIjwMHsV48OfhwZjvnAC0r2yJ3+NPhBP4g/GU14mpdefzvR08OElSHLpZidGsL5GGtpzcohM5sQ48TMsOs6Cy3vvgKR1oanGjGa8dRN+UaaAWm1dieSOjvXzIIVPp3zoKEgVu9zlP2W5NtNSVDfceVy/cA2IFjOlKa5EiLEEA57fuxvGmOvxCB+ZROvg6KOi6EbxLMylQEbvzctlbmEJ0S32x1usYisIWFfCLX/SEETVFuAxZJej9AcvkolOkSLNlohZdKzOYeRMfQM/RMT4JwSfFqHgIq4XeYPtTzMO2ZkTdOjdrrWL0ZMFosuXiKD/9qKKbo1FjqjwiT5a4uIaPdU95J52kiPoS7adOxUFiypbB9SrLFTABESJrPr0qMSVCi9cMME+Vt2Qq9gYFIvXoDRAR0SP04c/2A1r/tvxBu6JRGDB9cwYWOE1g8W+W/vju6WwPvifEO4AQ+KD3bGEhffrUWM1SnsAZBbJOgep/M1iU/HX4uNGb6Dmz+0PQdJAo7TkA2D+Wigyb9CQUfK16vwLvIIvMnylTcOOIAUtbiy2/lcdbmnQcFMt7ZZLQxBemf8S5L8jkyl1WLZyVNGDm5qf/72TQLs6KK4ljCJqMt0F6p8tidu/52WK95lYzKiZy6nlOSKadsCEWX5+eMzpJu8ZjYF5Qf1K54q5wO/T4Y+QYoWlUlXB6MoL0adwXmSs5T7Mht+6k8BO7T5I+3iI54WdYwixTnvlI/TNQSjwGJdxqJOmInihyKgkCx1lUyn/fx6jKZ+1MHPZwvfOg5V9TuCf+aXvjVhcgJHJBilS8ytrZh8FQh23yNbEIMoE6lYyWuYdSKv6831VdffGAP6gvaD3d9aUBJRkHquA1iqVB/ZG+bcJLpeMFJagd95AvGXUIuYwFKFmBtlKkjOuiEbKNKxv+SJ/NQCIGRBxVkm6oqcabuFnskNEhB4FnYnplnCIUZEfsuLirqsm6sSQZ2ZITdUAkmQ308cj5051V8FwogjNmZJyYuNNsOxYzumG33B7Z5k6QHkr2HC4aky5ZHP2bW8quZNaSXEcL5YGfZeTPTOVCv3TA+e4NLZeVocXTUYNWe7pyYjaf6EUeHdXOAMpZk9084KP8PBCwnlNfiZG2fXD+36bvn8sOVcsLvwAT01LEmVgo2E0geZqDPd8OIHJxDVB7VXNeFYIKjKgOjT63Bq49GLdBmwOlTKDljg00eYqLTQO66FPzSTWMc2EMGCae7sVr/OluTg/T4NKFt39gySNurVvPtlXZfqCo3GfCiyTV6iZWeuVMh69PrrozqgCX0mHJ+OyzMtQrTbqUB4BvHZe9Bfo/uyBDmRDWV0vTCz1mz0t+DTOjRkjEiAOFOKSQ5w/L3RgIwmuEgW3kqaQqtwAFIfWb9PxNuLvTLGMttZ3yO5P3aYl9G6jCSrrcr+3m0ICKOTBu8lH/lonRkZOq/08lpP5VtCEak6I+aSIT9tP9LJIZACn/IUe7qE88kjETKmnZT6F1D/1p58pEA0NI4g5CtdHlSXmg0s+zhAKS7tYpvNx96EPw5cCc5+VneGb0RDNvLaa+cEF4M/JuU0PcA9u9gu+PC+byS52tGqNA8yuH7El6JwFI8dXUvX07iAkC2VOvtt4kg0aeiHDyPHJpvvN4TaAH9Bz+WT5FDWNTAz4LC79GO6pQb9j5iojBlt+UUHvr8nfZN6AKa57RMsFTt9m0t0eBVUqR5fgpE/k6+57U9FtAQPZ5ufj66n0Ys1Chyr93K5jhX3GM64JjdryhghfffO150Q+hYrX3a5/fo2ULWBM27UoViPGVCFtmd0Yw1V5F+l8j58Mck1yUYxpU6tg+o1tara6THtW91V2dqC0+ha42qUVZhScMys1ygeqrpwVTvfhsaVH3/e0xXB7cO4UYkBg1ivB9O+90jwFfg1noBWOg7JpyGvPzYuLPz1CzNtVCqtRpqhMbCu4e2xQ++w8gJGD87TjODSjvgsXoDOs/Fs2qzhSatxvKrnW6pmKqwo9j4B12XZ4Sc+4oE2DIquGY8iyYrp9oBkSCQ8kOIkYVD74yj5C+Y/+JkFNVPwwBvarswkuyZUp8gjHCBLFkf0l+yBDWvJ/jZBXyUFSCGDIrpl1USocwndJFH5zst9/ZyaiKGKEO2nEBAuOCo1XTAyPLIjonN2pH7c01ySgFXymnEV0K0UGq78eDfUtxpmcGLtK+75NVraVGD2wNVNrpWJl1al+s+CM4OvabLcM6VnweXcGciDFRmghhWVoE4EqnhFUuFxCB3umtoyn8lKuEy1fmrRsweDOMtUNd0qA6IctHwIM0AOX2Sx0KxqjEhpp+YkfStkyLrzC33yJbUqRbgkDGq1fKfJDAdenpfQOVj6VMCsB208bbzJUcGOWzZtvfnETOnRLxb4LddrcPuP91CawvOVuAphNrIEUsiRon1SrCuL8GVF75tbSHcskqjIVLfycIZlvVjlywu9gBptiORxw/e1CZ7bDeKlTTIK67KQqosSEs1fnc/X0aAxlkqaOEZQdefKhrABuZFa/KTPRhQsFSncg6wI+niscy0rjfkkvg5fe4c17WCpa0eXot7t+4ot9O5+v0H/buYYniE4MzfrsDnJhqu1tLt1z0dNQ60Qz/8RxR7461d9KxJaNTelFLXDQwDHcTCBSk+0BrJVKT9Ls0bHgxr0zDoaDnbnlXjuu9+I+TH6sZYee1kDBqfPV/RKaXBx6yCFxEBosyCqvwmiuHUzItjvCMSpgREhM861FtvcyaGbN1+nFgM0NlPJQdpqz7bpEJcVw8HFp0yAAT61uYy8m51btG5zFKE74t+qEpjkQPOxPzxh52MDHVgMT0vIQcdA2GGXmjLInOlKHy44blBXKhSsvnWk6goe3xaY/vatI9iOJP0zdmqYuV/Z82spbMuwMwDVEEqrn/KPXqWl0G9AIAPPSA/DO5U9NZAn8nW5CcnB359CkSxVmBXbPBph/GvVrjZEiohjaAfRzdYgSBArwPcIhmfsE3ankfWrXOiw0qJgH4UvOuQphVkNCTIDl405MQMo+6Usm6YMkKx93V+wFSt0l6zoNYeELrp5hNwWNc35EVD0YJegiTIgVDqJykV3YM5po2UCDF4a1Ijhgu+mWL/+B3K8OcvmsGG8X/tKBCNPK/0jJT6PKfks/NEJDkcRcfm1ZDp9AFzldq53UZoT4o4zhRSpLA+f6VTIJx4/t78vpyZKMEJmc8RbIp/swFrbSGInwW4NCrovIK+oS5Z3zXeNbGSpuf2oWYAtpQvttaM2LNl4svcEwxvYor7JMy46l1f2SB0Q0PXLIehirHvMLhbfdWLQw0QB7Gq2O0khxvT1LjZ+H+euX7uZmkY9IvXdW0pnDhaNmZKT6nKj9K1bcLT3520W7lrdOzlEMHxtoSMMd9u2LtEkdtO0KIyfVvkXReY+ilkTyBUmcRCEWl27pABXdcl9jZn6A/16Ze1Lv9SFRncN42vpbOS3xkIBPtFwaDftP6IZLtchcxmj3xkeJFH8fFKg5f06HvCjPbxR3US46FTJqo49yM0H1L8wOjSC8wYHb4Mo6Zhh4i48snY9IOVfrIGqFfTsTQ5kxIctBPqGnMO7dl+iu4TUqeHkDk2IkmZSNjB7hp0mmLHKcTAB49JQDsZdlPlcOeADP/r7q/I5vXE8ZHzXqFmxW9v90+JMckU0V0AIrcJK9IQWl4LQR+dRuKRxJwDpy4wa4ymhqnBdjDMqQ/cetUExuVkzntiCPyOz6dMpAx9ZeidxQ02hYjPVqgFg8sCl1lTHTulvk7Nj698usBJMG+IKJorZp7+a97Tr226dW1h++Ic3ERIIDuFrJVY0UvO/vrTZrxZbzT2Ki+UvjN5Ins+P6gU7XLKlAlh4h3u54VXMJO6MqqpSFKXQlRY2fOOn/m5YDfOCvjmhsmrp63Wz9s+kowNsciO+DZa5Mce5qH9/ysvEHv7Sgb3AIZ4+zl1R9px1bU2HI/tcieQUvHkNG0N43uBelEbsrZTfVDAsk7KashZp+QG9k91BWuxlN00Hmaqd3foNx2EwoBe14MbFyJKr0PLJvFrMBQamhlWX31hknK3y9m7F3cIopvO2kIngxuVgZ/c3XOMnJysZcmgeVvouinM2GCcJF5k54InnSO0JJ0g4taICxSdD1NbXw4aVfuPXY2loCOKwXAsHW+vRvIu5yBYsAXeOX1J7LwWwVHOTLjQDRyIwgAsot1J4dr3tRO1u3s72SospfgKrMJdMYtrSJ6zvRQTEDXZcyk3fqtElG55syIjePTyPVPDGCGHVvaqOCWvYDXnsFAy9L3gVg8HaLMerTRuSzj6HjRmyZNheBBZkDOTRmc6yaJVhK/+NCpXgPsW3xyAX6ZGQ44NOAyn9U49Jz5VIUpEfXTK/hDaJeMgl/HmLcfxbBara5U+J5xi9IvwTcMMzxxN/sm/BjLc+34gP33ChIncbfHleQbbQvS6JMkySTA2PCbI/vwYonIZnymVtA3c4fC5zso+ZgTyvnxZkeJdDRPjTUtP6DFIAxMbIotg2e93CXfUp4ciADmTWa4IbuP3n602bqsqzTldZAt7UzolvY0gnTcmZWJC8dCoZhebkdcf9hd+jW/HdVo/YM6s39d1Mqm7PnG2dsXFSCn+yg1redbnDTPpUVi1+T1xd6dGeM7GddroA/qyNLl9dvdvCUGQvRL7BIFQFUZYXRdx27OAStt+iqORvuibZWfLufrRJVM6AoyJNpRo4rALSdtAcfW8d4HJGPEaP1cxl6ErnQz+yDbv+zRMTFCJiuPTJRDXD+ir8hz+eChUN323YpgVJ0Qjl9oqEj9H3SKORfnFaq0337C3oyz0eQ5PedG/d78nJzRP+BfQIOFMDzPSJ40yg+MAgX0P6ZPOiBIW7c/i2j6TQhVyeEUzsjRMYMMiGQl/lgTz9D6Kc/WP4tzbzhRb0Icoy5+sZRiap1rQFjaOVzGUEOXgMoME9voaumyWcTskYTxGdil9CvKBKsHCFx8iZ63V1xcmT2JnOVuYEAqOwD6bSc6KhJznv+nSyG7HNY+ycCXP1NBoG5Z8QgXEcJxUMl0SDUaMAqM4K/NL+ZiQHDbDL38U9eBa9zYaG7xronBtZ7ieC2yMOcMfz4tSvATwPeH+qlTOJQjBtFEzHkFV84bUdVYLaMj8/oM+rVU/4hZCpXR42AXjhfEZBT2M4YZv9ciCjNAo63zbfTv2zt7A6ZYVUkRFW3mRQw0EP7bmK8w4BcVzhy2U0zaJqlBAbc1i/4A+0lmSnyKBISJRF4lrGz1dIsCpZ5AeuDopJNc59Rb7viBjmnA5rBqdrxPhNnReYbJd2k3g7YPAV21Hx4wf7oUsVn8Mu6dgmChDCc1IEc9jxSnHYCWqlCA7YBeUtXTXIJf2qe7knGliksYKnYfX9RnXdeDoIbmKWGsV2mnK+oJPzOlF46TC391bf9GBe8T2rvcXJINCfZBmS60iO+5Yo2NNJQi+Qc9SebaaygxTZOj6rIbNwzdhDEUYCG8zfS9KmEhZKfcz5+9oCIG6mM8oh7q79yxzDIzdpaotBKCgJ9M8jtC/Ee5ZI8adPdXMkB1EEzaGWZBuBvzecpPmTyhzpKBy8FB0kKhEOjY0/utP7JAJKpId0xWuDDsFlSsbCqPgb4wbUqID7Qxu6FUJ1QGCxGYA+u/NXFQesgGrYlWKdm0zY62gtlUv89zV1PwQwB4TNtP16MrfZAuYhqgR2xJ7ON7tWJ49lVyjB5NbzlCGelLKJIkoicwMz1CSQ8b9SO2qk+WMWUPnXqCsHBSU7ews5rZ8ccw539tfEBj9UNPUqW30tjb9BIc5q0ypPa15S8ucZOGEpSGyRLaf8SdSxw1JDsq0vYF04PoWvvYyAIAVNl6ACzWEnCPSzVAb2orLKO2McQpRAY4I762BRDhBt0R6a1Qm9Hx9g0gUfQE6iXBniPe81OUTKzGHNKxHzV2sP3HgVlBmB2M3N2tJTzb65XnRGKLGOgMe2/eVvLj54lK4MRe5vTJG1QvZUKbxnK0YdMNE/N/eTPwJ3tB7tMyVVVDEUQpzKNtWqrbKvtQcxG1Dy42DjnsCW+DNlXdgmIKcG8ZpJT9vTihoR2UAK1ZG1WPhVF2oNNvQGU3z3hIQ8VNmdu0EMJlEu6v4iTlLYi3E68RpLs8Eq1d6csi6nKrJRssSwsm8ApR/yO/p9c7dYj4EsfcwhxzsfgLdpu8SKZUUgHkSs+KWA2F3fHUawrHUZvl4xdkDqC/S4vi8CweW7ed/VvuriZXHgljCahrwhe2YRn0rZl3Kvsc3wz2L8XaRhusY1lT5Xy8rqsCiKFcuevI7DUCV2/c3uuhY08+5+qTihQwGlrJTQo8iTNr39o6lcoalqyKYeXWoQEKpUQP/SvTT5qhq+7NdJoB+q9JkU+q0aEQwqBOF+rdmRUeYEMWXmPiJ7NndcQGuAJg+M5pnbB25DUv2zP2Xqj/PjYypAJMMavI7YgoIlZ6VZ/L1yqU+PlABLp7+A93JgpG0hv221lEPIWY4+RNr3yyhPnCxtGA8obgUDu/6FIHqq+hxm+GfZx2DI2TQjgQs5yJiUyIVoXbmjjoBX0axEn1x3xsa7YlGVeFw1jeqFbgdIFN+KInG4kpJVd07c4BLJiITZFodHExoFD65tsX1SLXpZgdoljKwDo2DkacLCLiaV8PShqJEjo58uXdCu676mtSePbGyW0KZigAPGEpUEZ6zc1l9cZXjeDi2aLJpl6sphMR/B5aiIz6J7Afj3feUuq5qxxFHQC8jR1C1hPV7ZxF7Sub+U5iB+ynvUkt4iJd7kxJDARVbZPBbUSb9/ny0nBbzZmkRE6oi+0ocWxaH4ZnVrsL/NgnFPwKuG2IwbNCHls26kUeON7qS/+j0PLAXzBghwiRgBku1clT/tM30AS1mvJ6cKDjjLPMei7GwGHaJFfQqEjjikb7ktX5O1jVMlZTrNGliwOK1fTh3jE9b5K9AppT5IFuPxhbJ97+HMazBEPtMA9aZBIKXNFIvdPPCs0DHt05HzygjrejibsBA/SS2F+gSlANRlkrJinMIpt/gdlvUbjaxFrMupGmVCoMDfRDrxO053FTh8nto2pA2ActBghuqLM8p91U5FtVhXU+FI8whYX5WdWMmWc2E2wGzFz1aCKYJIC/qr4xzN305xQLxAVb2n0BQedGI+j38cc0ECk1NxJ2isVKvmhk5RyzSc6EPzB1884xko7roUM7NOu0FiPw+Zu4R8OGoHRYqsigkTRxlmL19aGEbBbdK9TmGBvwCd307SHj2GojSWN7DL9olp1+VMMYQ9UG8DTX47r23qkXZ4z3ctQl86rRjpzdj+70XvZb+h0FzgnyJmYSHxIIn2FWNYmvwPjyiBUgHYP5RoHhSJoeI6W+nkFnHijreTncsonIU5FKlqHQFGzzdc8s9U5sfrMFtR1SUYFYWj3C8KP0oQwiXZcn3AcqPkTqVU0o5kRZ2+QS+fJP1ozNeh6hKJSpUVSb2LZ9329cfBOPAJ7u8zYUqJZ8CIzIa26Qy5ADf5bco2Z18IcLHAulDYBXxaBCm2DXpryNEQMYWmMTHA0mVpIFVkmU5dfnNQykdZiAXU1l+Fw6kIjrMJ9AgF0xWiaZnOyTehWtuxU47hvUm8B2A9ociq2x5aFOxazc3YG5IB7IZmXercFhEWIMzMw63jvREmRjCT5ou+MIjmbi1na8d0SaLUudX5pUouPbc+4stjuNveU6cNACO0s+nbAlVyZyCeRMAPk5C+11kHcwSNd8IZugXSih5eJ4xPoIW0knz0365CjhNUfz9+31qYzK0lZNMUCuf2K0vrUBB/i3T3gdXMGSeldKp3Lx+tz/bpKXTHtUzzsvdS9Gs+uMIZ1XK6AxFyeCxOJ+cU9XN1fBnLPe2JYUlJUmCu4tiwsprlamaRzZQNWlUxombEZeKC7q3mwHcZM5wU0ICwEnLfTxW0VL9N10+batqOKxQnIspanPsw1ez2cuwr/hQSPXqoP2gIkFZnmAqUKUX8GZ5ib+C60pulz4Uxz/QvZW7V2SAAGcUwS30VsW6U2Ld2v5UbOfEQCxPdOHJZw75sKgEdyVdN1FDl4JC6s8IUclP+LD6R/CXIEDhbSWuXdTsAinSZLlMH1LzCXp6Cqvih/NReD6FJezE4Hi0sUGxti+4YngNBTWhUOblVY4+ioJs/kpVyXoAksKXh+Fe1j1PG2gbHkCQQWWCDqufQCEypj+dCoj37UreY26CogoUkVCnNUXQ5jZNFOPeXjh336gUEGzTt9qLgRwsxEJpQKH+aCWZALuJHtCVlK1WQMM6eM15EjMtRabejRb7eD3Us4WqESLYxpZ5KCobtmQDzV/4vOlvq0BSClPNORXWKygxQ2J9casayyd9DxvL77P41vt3k3fsT5PB1d6WR+6JZWwYJGZTdxyDyiFJDCKV9TuCeGkZQ26g1V0sV/H5a1xciwxOCNt7GgQOajs3aR4wpXxg4GbU0nOR0c9Ii/Sn27VMt4BqnAj5W4fx8q4ecJlPHlG3tSjqKSUsP0rlyg7JRFXcxCUGv7QMYc2K9WLvLEHbBOcM/ZD87o+UaQ3CvTwOkQTDq8hUeOBRxcerQV5Xi6Y+Hh6Vg4aeMpoGdUV7xXbw5oVh/mkSLP70aWsGQ3UbqZLFHrxQzLeDFkYJX6q069Lp/1X+lGTY+5ykXDRtK1n+GarP5tNWi4nd81eFXdracJWwcYk2GA6MbdjMnoaTrfSHXO3EXgrlq6ko5DABSrMg+9kF88aW5LAVOxGADYFS8bniGvdKVXnEhhQDJVCYKqqWKYGpAek5BGeVRWSbwLCKdQ5BcBnn+oEsmp46uK3k8KO72Pn+1hPMbgE6xWxVYPqAe7HVPPjNRiQS6cQGOxU1gdlAuEJ4V7ip4o+TgDM2/M4bthC6c4SBMQaMfRZfL5ko/uf3U2MXch54RJ2/LQRAy3AHiOI6enjY+L88VIvjU+hnmwro8yEflSD4tEMeFIkrxEW19Gycl1BDXpDVbs9nrU5MMIGx6QxCFw8FibHOtcRcI71o8s+OvDCQFsw7ZVMslGVDaprGZZmJ2j4uTgxrn15ihGv020yixBNktFCYgTyPlxA1f36ciarunxld8CPUVUPV/D/XFX5s/Neg2cdPqmSlO/fpnXxz4UJnIlB6hSl82wNGKJud1KoVyDHmmjI+EKBSUO7kNuvrQ/fY3duE75BX/HUAeUiLFKBZ1O2/mThw8t0Wq782ApG12/Jvza+94ENybWDDpLLmTddfEP7cYjFtZZONpGuxNkP8FAAD//w==" + ]; + + // Test that fixes #191501. Ensures compressed data can be streamed out correctly. + #[test] + fn test_flatestream_decodes_191501() { + let mut dec = ClientMessageDecoder::new_compressed(); + let mut len = 0; + for b in TEST_191501_BUFS { + let b = general_purpose::STANDARD + .decode(b) + .expect("expected no decode error"); + let s = dec.decode(&b).expect("expected no decompress error"); + len += s.len(); + } + + assert_eq!(len, 265 + 101 + 10370); + } } From 87fd7b79d9fa8eb8bce41dbb74617c20074aca3f Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 28 Aug 2023 13:02:11 -0700 Subject: [PATCH 287/607] Update workspace trust dialog for ai generated workspaces (#191474) * Update workspace trust options for ai generated workspaces * Make this.productService.aiGeneratedWorkspaceTrust optional --- src/vs/base/common/product.ts | 9 ++++ .../browser/workspace.contribution.ts | 53 ++++++++++++++++--- 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 47b2de558c4..ea129e9298a 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -186,6 +186,7 @@ export interface IProductConfiguration { readonly profileTemplatesUrl?: string; readonly commonlyUsedSettings?: string[]; + readonly aiGeneratedWorkspaceTrust?: IAiGeneratedWorkspaceTrust; } export interface ITunnelApplicationConfig { @@ -279,3 +280,11 @@ export interface ISurveyData { editCount: number; userProbability: number; } + +export interface IAiGeneratedWorkspaceTrust { + readonly title: string; + readonly checkboxText: string; + readonly trustOption: string; + readonly dontTrustOption: string; + readonly startupTrustRequestLearnMore: string; +} diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 8bc95f4f591..a488d01724c 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -47,6 +47,9 @@ import { isWeb } from 'vs/base/common/platform'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { securityConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { basename, dirname as uriDirname } 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'; const BANNER_RESTRICTED_MODE = 'workbench.banner.restrictedMode'; const STARTUP_PROMPT_SHOWN_KEY = 'workspace.trust.startupPrompt.shown'; @@ -237,6 +240,8 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon @IHostService private readonly hostService: IHostService, @IProductService private readonly productService: IProductService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IFileService private readonly fileService: IFileService, ) { super(); @@ -303,10 +308,26 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon this.updateWorkbenchIndicators(trusted); })); - this._register(this.workspaceTrustRequestService.onDidInitiateWorkspaceTrustRequestOnStartup(() => { - const title = this.useWorkspaceLanguage ? + this._register(this.workspaceTrustRequestService.onDidInitiateWorkspaceTrustRequestOnStartup(async () => { + + let titleString: string | undefined; + let checkboxString: string | undefined; + let learnMoreString: string | undefined; + let trustOption: string | undefined; + let dontTrustOption: string | undefined; + if (await this.isAiGeneratedWorkspace() && this.productService.aiGeneratedWorkspaceTrust) { + titleString = this.productService.aiGeneratedWorkspaceTrust.title; + checkboxString = this.productService.aiGeneratedWorkspaceTrust.checkboxText; + learnMoreString = this.productService.aiGeneratedWorkspaceTrust.startupTrustRequestLearnMore; + trustOption = this.productService.aiGeneratedWorkspaceTrust.startupTrustRequestLearnMore; + dontTrustOption = this.productService.aiGeneratedWorkspaceTrust.dontTrustOption; + } else { + console.warn('AI generated workspace trust dialog contents not available.'); + } + + const title = titleString ?? (this.useWorkspaceLanguage ? localize('workspaceTrust', "Do you trust the authors of the files in this workspace?") : - localize('folderTrust', "Do you trust the authors of the files in this folder?"); + localize('folderTrust', "Do you trust the authors of the files in this folder?")); let checkboxText: string | undefined; const workspaceIdentifier = toWorkspaceIdentifier(this.workspaceContextService.getWorkspace()); @@ -314,19 +335,19 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon const isEmptyWindow = isEmptyWorkspaceIdentifier(workspaceIdentifier); if (this.workspaceTrustManagementService.canSetParentFolderTrust()) { const name = basename(uriDirname((workspaceIdentifier as ISingleFolderWorkspaceIdentifier).uri)); - checkboxText = localize('checkboxString', "Trust the authors of all files in the parent folder '{0}'", name); + checkboxText = checkboxString ?? localize('checkboxString', "Trust the authors of all files in the parent folder '{0}'", name); } // Show Workspace Trust Start Dialog this.doShowModal( title, - { label: localize({ key: 'trustOption', comment: ['&& denotes a mnemonic'] }, "&&Yes, I trust the authors"), sublabel: isSingleFolderWorkspace ? localize('trustFolderOptionDescription', "Trust folder and enable all features") : localize('trustWorkspaceOptionDescription', "Trust workspace and enable all features") }, - { label: localize({ key: 'dontTrustOption', comment: ['&& denotes a mnemonic'] }, "&&No, I don't trust the authors"), sublabel: isSingleFolderWorkspace ? localize('dontTrustFolderOptionDescription', "Browse folder in restricted mode") : localize('dontTrustWorkspaceOptionDescription', "Browse workspace in restricted mode") }, + { label: trustOption ?? localize({ key: 'trustOption', comment: ['&& denotes a mnemonic'] }, "&&Yes, I trust the authors"), sublabel: isSingleFolderWorkspace ? localize('trustFolderOptionDescription', "Trust folder and enable all features") : localize('trustWorkspaceOptionDescription', "Trust workspace and enable all features") }, + { label: dontTrustOption ?? localize({ key: 'dontTrustOption', comment: ['&& denotes a mnemonic'] }, "&&No, I don't trust the authors"), sublabel: isSingleFolderWorkspace ? localize('dontTrustFolderOptionDescription', "Browse folder in restricted mode") : localize('dontTrustWorkspaceOptionDescription', "Browse workspace in restricted mode") }, [ !isSingleFolderWorkspace ? localize('workspaceStartupTrustDetails', "{0} provides features that may automatically execute files in this workspace.", this.productService.nameShort) : localize('folderStartupTrustDetails', "{0} provides features that may automatically execute files in this folder.", this.productService.nameShort), - localize('startupTrustRequestLearnMore', "If you don't trust the authors of these files, we recommend to continue in restricted mode as the files may be malicious. See [our docs](https://aka.ms/vscode-workspace-trust) to learn more."), + learnMoreString ?? localize('startupTrustRequestLearnMore', "If you don't trust the authors of these files, we recommend to continue in restricted mode as the files may be malicious. See [our docs](https://aka.ms/vscode-workspace-trust) to learn more."), !isEmptyWindow ? `\`${this.labelService.getWorkspaceLabel(workspaceIdentifier, { verbose: Verbosity.LONG })}\`` : '', ], @@ -436,6 +457,24 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon return !isSingleFolderWorkspaceIdentifier(toWorkspaceIdentifier(this.workspaceContextService.getWorkspace())); } + private async isAiGeneratedWorkspace(): Promise { + const aiGeneratedWorkspaces = URI.joinPath(this.environmentService.workspaceStorageHome, 'aiGeneratedWorkspaces.json'); + return await this.fileService.exists(aiGeneratedWorkspaces).then(async result => { + if (result) { + try { + const content = await this.fileService.readFile(aiGeneratedWorkspaces); + const workspaces = JSON.parse(content.value.toString()) as string[]; + if (workspaces.indexOf(this.workspaceContextService.getWorkspace().folders[0].uri.toString()) > -1) { + return true; + } + } catch (e) { + // Ignore errors when resolving file contents + } + } + return false; + }); + } + //#endregion //#region Banner From c70624f23b3b88f54e236da0c893624a83ec1d73 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 13:10:05 -0700 Subject: [PATCH 288/607] add go to symbol placeholder for accessible view --- src/vs/workbench/contrib/accessibility/browser/accessibleView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 2c7b4780c06..42e95d10c93 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -583,6 +583,7 @@ class AccessibleViewSymbolQuickPick { } show(provider: IAccessibleContentProvider): void { const quickPick = this._quickInputService.createQuickPick(); + quickPick.placeholder = localize('accessibleViewSymbolQuickPickPlaceholder', "Type to search symbols"); quickPick.title = localize('accessibleViewSymbolQuickPickTitle', "Go to Symbol Accessible View"); const picks = []; const symbols = this._accessibleView.getSymbols(); From 6dd57c91d05a84dd430ddf177fca0f8915e37eaf Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 13:16:06 -0700 Subject: [PATCH 289/607] change none description --- .../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 b997c5e3c72..1e097672b6b 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -618,7 +618,7 @@ const terminalConfiguration: IConfigurationNode = { localize('terminal.integrated.focusAfterRun.auto', "Set to `terminal` when in screen reader optimized mode and `none` otherwise."), localize('terminal.integrated.focusAfterRun.terminal', "Always focus the terminal."), localize('terminal.integrated.focusAfterRun.accessible-buffer', "Always focus the accessible buffer."), - localize('terminal.integrated.focusAfterRun.none', "Keep the focus in the editor."), + localize('terminal.integrated.focusAfterRun.none', "Do nothing."), ] } } From 8d43226a4a92db3e78e90642a4a03919458a8bf4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Mon, 28 Aug 2023 13:20:51 -0700 Subject: [PATCH 290/607] add requestFocus --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 1 + .../workbench/contrib/terminal/browser/terminalActions.ts | 7 +++---- .../browser/terminal.accessibility.contribution.ts | 3 +++ 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 28b600107aa..bba8941c31b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -40,6 +40,7 @@ export const ITerminalInstanceService = createDecorator Date: Mon, 28 Aug 2023 13:35:46 -0700 Subject: [PATCH 291/607] fix #188329 --- .../browser/terminal.accessibility.contribution.ts | 4 ++-- .../browser/terminalAccessibleBuffer.ts | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 7fd8369f2ea..f404c684fbd 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -116,8 +116,8 @@ registerTerminalAction({ precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), keybinding: [ { - primary: KeyMod.Shift | KeyCode.Tab, - secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.Alt | KeyCode.F2], + primary: KeyMod.Alt | KeyCode.F2, + secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow], weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.focus, ContextKeyExpr.or(terminalTabFocusModeContextKey, TerminalContextKeys.accessibleBufferFocus.negate())) } diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts index 41992fae298..8e6c44210bb 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBuffer.ts @@ -62,18 +62,6 @@ export class AccessibleBufferWidget extends TerminalAccessibleWidget { this.element.ariaRoleDescription = localize('terminal.integrated.accessibleBuffer', 'Terminal buffer'); _instance.onDidRequestFocus(() => this.hide(true)); this.updateEditor(); - this.add(this.editorWidget.onDidFocusEditorText(async () => { - if (this.element.classList.contains(ClassName.Active)) { - // the user has focused the editor via mouse or - // Go to Command was run so we've already updated the editor - return; - } - // if the editor is focused via tab, we need to update the model - // and show it - this.registerListeners(); - await this.updateEditor(); - this.element.classList.add(ClassName.Active); - })); // xterm's initial layout call has already happened this.layout(); } From fd4d801227201d7b288b6f2547fd6f607c644daa Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 28 Aug 2023 23:31:54 +0200 Subject: [PATCH 292/607] [css/json/html] update dependencies (#191522) --- extensions/css-language-features/package.json | 2 +- .../css-language-features/server/package.json | 6 +- .../css-language-features/server/yarn.lock | 56 +-- extensions/css-language-features/yarn.lock | 38 +- .../html-language-features/package.json | 4 +- .../server/package.json | 8 +- .../html-language-features/server/yarn.lock | 66 +-- extensions/html-language-features/yarn.lock | 414 +++++++++++++----- .../json-language-features/package.json | 4 +- .../server/package.json | 6 +- .../json-language-features/server/yarn.lock | 61 ++- extensions/json-language-features/yarn.lock | 414 +++++++++++++----- 12 files changed, 715 insertions(+), 364 deletions(-) diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index b267163da39..e2fed901bce 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -994,7 +994,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^8.2.0-next.1", + "vscode-languageclient": "^8.2.0-next.3", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index bdb66297271..874ce1e8202 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -10,9 +10,9 @@ "main": "./out/node/cssServerMain", "browser": "./dist/browser/cssServerMain", "dependencies": { - "@vscode/l10n": "^0.0.14", - "vscode-css-languageservice": "^6.2.6", - "vscode-languageserver": "^8.2.0-next.1", + "@vscode/l10n": "^0.0.16", + "vscode-css-languageservice": "^6.2.7", + "vscode-languageserver": "^8.2.0-next.3", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 649160819e3..296912060e4 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -12,55 +12,55 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/l10n@^0.0.14": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.14.tgz#431e5814c35c3cb11ee21873bc70a4b0fbf90fcf" - integrity sha512-/yrv59IEnmh655z1oeDnGcvMYwnEzNzHLgeYcQCkhYX0xBvYWrAuefoiLcPBUkMpJsb46bqQ6Yv4pwTTQ4d3Qg== +"@vscode/l10n@^0.0.16": + version "0.0.16" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" + integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== -vscode-css-languageservice@^6.2.6: - version "6.2.6" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.6.tgz#bc26c2abaaa2eb117b143fdb9387ee1701d9661a" - integrity sha512-SA2WkeOecIpUiEbZnjOsP/fI5CRITZEiQGSHXKiDQDwLApfKcnLhZwMtOBbIifSzESVcQa7b/shX/nbnF4NoCg== +vscode-css-languageservice@^6.2.7: + version "6.2.7" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.7.tgz#d64e347e9a432d2b9c1a12d1ea5bc77996a2e9dc" + integrity sha512-Jd8wpIg5kJ15CfrieoEPvu3gGFc36sbM3qXCtjVq5zrnLEX5NhHxikMDtf8AgQsYklXiDqiZLKoBnzkJtRbTHQ== dependencies: - "@vscode/l10n" "^0.0.14" + "@vscode/l10n" "^0.0.16" vscode-languageserver-textdocument "^1.0.8" vscode-languageserver-types "^3.17.3" vscode-uri "^3.0.7" -vscode-jsonrpc@8.2.0-next.0: - version "8.2.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.0.tgz#41409413c8cebf10f2f1b7cc87e330f0e292814c" - integrity sha512-13jYzaFQpTz5qQ2P+l5c/iTVsj1wUpflP0CR/v4XaEpM0oToLEXZBTcuuox1WaGIbu3Av3xxmGNU4Hydl1iNKg== +vscode-jsonrpc@8.2.0-next.2: + version "8.2.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.2.tgz#09d72832353fc7fb43b33c9c68b083907f6a8a68" + integrity sha512-1FQrqLselaLLe5ApFSU/8qGUbJ8tByWbqczMkT2PEDpDYthCQTe5wONPuVphe7BB+FvZwvBFI2kFkY7FtyHc1A== -vscode-languageserver-protocol@3.17.4-next.1: - version "3.17.4-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.1.tgz#a15480e1bc663853ae90ded226efafc5ab333616" - integrity sha512-qrK4BycgPR/+nkRN9PRVTblkLp+kUPUmAgF6rDhFzZIPXW4/MqWwFUT8uswIMGdlTPPgCEkFO/AYEZK1fDXODg== +vscode-languageserver-protocol@3.17.4-next.3: + version "3.17.4-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.3.tgz#7d1d4fcaaa3213a8f2b8a6f1efa8187163251b7c" + integrity sha512-GnW3ldfzlsDK9B1/L1edBW1ddSakC59r+DRipTYCcXIT/zCCbLID998Dxn+exgrL33e3/XLQ+7hQQiSz6TnhKQ== dependencies: - vscode-jsonrpc "8.2.0-next.0" - vscode-languageserver-types "3.17.4-next.0" + vscode-jsonrpc "8.2.0-next.2" + vscode-languageserver-types "3.17.4-next.2" vscode-languageserver-textdocument@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz#9eae94509cbd945ea44bca8dcfe4bb0c15bb3ac0" integrity sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q== -vscode-languageserver-types@3.17.4-next.0: - version "3.17.4-next.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.0.tgz#4b5238d21cceaeb836d36a05d23c61a8c0238de2" - integrity sha512-2FPKboHnT04xYjfM8JpJVBz4a/tryMw58jmzucaabZMZN5hzoFBrhc97jNG4n6edr9JUb9+QSwwcAcYpDTAoag== +vscode-languageserver-types@3.17.4-next.2: + version "3.17.4-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.2.tgz#4099ff39b38edbd2680df13bfb1c05f0c07bfe8d" + integrity sha512-r6tXyCXyXQH7b6VHkvRT0Nd9v+DWQiosgTR6HQajCb4iJ1myr3KgueWEGBF1Ph5/YAiDy8kXUhf8dHl7wE1H2A== vscode-languageserver-types@^3.17.3: version "3.17.3" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz#72d05e47b73be93acb84d6e311b5786390f13f64" integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA== -vscode-languageserver@^8.2.0-next.1: - version "8.2.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.2.0-next.1.tgz#ad2558d74392b1cfaccd427febe9a368fc328f8b" - integrity sha512-994AXMKBijzjlnpf8p9M+ntsNJDjR8pr55NJPYxKjy/nUhVkg962dAomelH6Z94401kBZmSbfP/K/20cB54aFA== +vscode-languageserver@^8.2.0-next.3: + version "8.2.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.2.0-next.3.tgz#72e4998392260173fb0c35d2d556fb4015f56ce3" + integrity sha512-fqHRwcIRoxfKke7iLDSeUmdo3uk7o/uWNn/44xdWa4urdhsvpTZ5c1GsL1EX4TAvdDg0qeXy89NBZ5Gld2DkgQ== dependencies: - vscode-languageserver-protocol "3.17.4-next.1" + vscode-languageserver-protocol "3.17.4-next.3" vscode-uri@^3.0.7: version "3.0.7" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index acd761d8f5b..826e0bb3306 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -40,32 +40,32 @@ semver@^7.3.7: dependencies: lru-cache "^6.0.0" -vscode-jsonrpc@8.2.0-next.0: - version "8.2.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.0.tgz#41409413c8cebf10f2f1b7cc87e330f0e292814c" - integrity sha512-13jYzaFQpTz5qQ2P+l5c/iTVsj1wUpflP0CR/v4XaEpM0oToLEXZBTcuuox1WaGIbu3Av3xxmGNU4Hydl1iNKg== +vscode-jsonrpc@8.2.0-next.2: + version "8.2.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.2.tgz#09d72832353fc7fb43b33c9c68b083907f6a8a68" + integrity sha512-1FQrqLselaLLe5ApFSU/8qGUbJ8tByWbqczMkT2PEDpDYthCQTe5wONPuVphe7BB+FvZwvBFI2kFkY7FtyHc1A== -vscode-languageclient@^8.2.0-next.1: - version "8.2.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.2.0-next.1.tgz#a3f98b80cfa3225fde0583aa6a5c9b20219fa37e" - integrity sha512-oITaqHQ10PM3zXCUu/104wriMeDutXMkQXMaRBWh1jKihcNcUBLC/os7RhqiVGypY0nl+F0pwStAf4Koc8inaw== +vscode-languageclient@^8.2.0-next.3: + version "8.2.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.2.0-next.3.tgz#a5086f451a679ce77106d8fd1e05c8cbf8e9b886" + integrity sha512-Ojo6L2cb7GSiyD864k8vGb9fHxBdZeciHQQOF595C3IDHWg0w4KQ7iN7qGWVdl4wDNwlGTX3wWZawGfPTxnrPQ== dependencies: minimatch "^5.1.0" semver "^7.3.7" - vscode-languageserver-protocol "3.17.4-next.1" + vscode-languageserver-protocol "3.17.4-next.3" -vscode-languageserver-protocol@3.17.4-next.1: - version "3.17.4-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.1.tgz#a15480e1bc663853ae90ded226efafc5ab333616" - integrity sha512-qrK4BycgPR/+nkRN9PRVTblkLp+kUPUmAgF6rDhFzZIPXW4/MqWwFUT8uswIMGdlTPPgCEkFO/AYEZK1fDXODg== +vscode-languageserver-protocol@3.17.4-next.3: + version "3.17.4-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.3.tgz#7d1d4fcaaa3213a8f2b8a6f1efa8187163251b7c" + integrity sha512-GnW3ldfzlsDK9B1/L1edBW1ddSakC59r+DRipTYCcXIT/zCCbLID998Dxn+exgrL33e3/XLQ+7hQQiSz6TnhKQ== dependencies: - vscode-jsonrpc "8.2.0-next.0" - vscode-languageserver-types "3.17.4-next.0" + vscode-jsonrpc "8.2.0-next.2" + vscode-languageserver-types "3.17.4-next.2" -vscode-languageserver-types@3.17.4-next.0: - version "3.17.4-next.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.0.tgz#4b5238d21cceaeb836d36a05d23c61a8c0238de2" - integrity sha512-2FPKboHnT04xYjfM8JpJVBz4a/tryMw58jmzucaabZMZN5hzoFBrhc97jNG4n6edr9JUb9+QSwwcAcYpDTAoag== +vscode-languageserver-types@3.17.4-next.2: + version "3.17.4-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.2.tgz#4099ff39b38edbd2680df13bfb1c05f0c07bfe8d" + integrity sha512-r6tXyCXyXQH7b6VHkvRT0Nd9v+DWQiosgTR6HQajCb4iJ1myr3KgueWEGBF1Ph5/YAiDy8kXUhf8dHl7wE1H2A== vscode-uri@^3.0.7: version "3.0.7" diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 58bd75f70e9..422fce5e7f6 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -258,8 +258,8 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.7.5", - "vscode-languageclient": "^8.2.0-next.1", + "@vscode/extension-telemetry": "^0.8.4", + "vscode-languageclient": "^8.2.0-next.3", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 3304c243b35..1816dca28f5 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,10 +9,10 @@ }, "main": "./out/node/htmlServerMain", "dependencies": { - "@vscode/l10n": "^0.0.14", - "vscode-css-languageservice": "^6.2.6", - "vscode-html-languageservice": "^5.0.6", - "vscode-languageserver": "^8.2.0-next.1", + "@vscode/l10n": "^0.0.16", + "vscode-css-languageservice": "^6.2.7", + "vscode-html-languageservice": "^5.0.7", + "vscode-languageserver": "^8.2.0-next.3", "vscode-languageserver-textdocument": "^1.0.8", "vscode-uri": "^3.0.7" }, diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index fe6c91e3cf8..22f945e7f84 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -12,65 +12,65 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/l10n@^0.0.14": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.14.tgz#431e5814c35c3cb11ee21873bc70a4b0fbf90fcf" - integrity sha512-/yrv59IEnmh655z1oeDnGcvMYwnEzNzHLgeYcQCkhYX0xBvYWrAuefoiLcPBUkMpJsb46bqQ6Yv4pwTTQ4d3Qg== +"@vscode/l10n@^0.0.16": + version "0.0.16" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" + integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== -vscode-css-languageservice@^6.2.6: - version "6.2.6" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.6.tgz#bc26c2abaaa2eb117b143fdb9387ee1701d9661a" - integrity sha512-SA2WkeOecIpUiEbZnjOsP/fI5CRITZEiQGSHXKiDQDwLApfKcnLhZwMtOBbIifSzESVcQa7b/shX/nbnF4NoCg== +vscode-css-languageservice@^6.2.7: + version "6.2.7" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.7.tgz#d64e347e9a432d2b9c1a12d1ea5bc77996a2e9dc" + integrity sha512-Jd8wpIg5kJ15CfrieoEPvu3gGFc36sbM3qXCtjVq5zrnLEX5NhHxikMDtf8AgQsYklXiDqiZLKoBnzkJtRbTHQ== dependencies: - "@vscode/l10n" "^0.0.14" + "@vscode/l10n" "^0.0.16" vscode-languageserver-textdocument "^1.0.8" vscode-languageserver-types "^3.17.3" vscode-uri "^3.0.7" -vscode-html-languageservice@^5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.0.6.tgz#e7a7f78e9f98d0f5341c5518dd9305e3cc438bb6" - integrity sha512-gCixNg6fjPO7+kwSMBAVXcwDRHdjz1WOyNfI0n5Wx0J7dfHG8ggb3zD1FI8E2daTZrwS1cooOiSoc1Xxph4qRQ== +vscode-html-languageservice@^5.0.7: + version "5.0.7" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.0.7.tgz#8d27773e0197799a9db777ee4fc134cf1c669d84" + integrity sha512-jX+7/kUXrdOaRT8vqYR/jLxrGDib+Far8I7n/A6apuEl88k+mhIHZPwc6ezuLeiCKUCaLG4b0dqFwjVa7QL3/w== dependencies: - "@vscode/l10n" "^0.0.14" + "@vscode/l10n" "^0.0.16" vscode-languageserver-textdocument "^1.0.8" vscode-languageserver-types "^3.17.3" vscode-uri "^3.0.7" -vscode-jsonrpc@8.2.0-next.0: - version "8.2.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.0.tgz#41409413c8cebf10f2f1b7cc87e330f0e292814c" - integrity sha512-13jYzaFQpTz5qQ2P+l5c/iTVsj1wUpflP0CR/v4XaEpM0oToLEXZBTcuuox1WaGIbu3Av3xxmGNU4Hydl1iNKg== +vscode-jsonrpc@8.2.0-next.2: + version "8.2.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.2.tgz#09d72832353fc7fb43b33c9c68b083907f6a8a68" + integrity sha512-1FQrqLselaLLe5ApFSU/8qGUbJ8tByWbqczMkT2PEDpDYthCQTe5wONPuVphe7BB+FvZwvBFI2kFkY7FtyHc1A== -vscode-languageserver-protocol@3.17.4-next.1: - version "3.17.4-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.1.tgz#a15480e1bc663853ae90ded226efafc5ab333616" - integrity sha512-qrK4BycgPR/+nkRN9PRVTblkLp+kUPUmAgF6rDhFzZIPXW4/MqWwFUT8uswIMGdlTPPgCEkFO/AYEZK1fDXODg== +vscode-languageserver-protocol@3.17.4-next.3: + version "3.17.4-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.3.tgz#7d1d4fcaaa3213a8f2b8a6f1efa8187163251b7c" + integrity sha512-GnW3ldfzlsDK9B1/L1edBW1ddSakC59r+DRipTYCcXIT/zCCbLID998Dxn+exgrL33e3/XLQ+7hQQiSz6TnhKQ== dependencies: - vscode-jsonrpc "8.2.0-next.0" - vscode-languageserver-types "3.17.4-next.0" + vscode-jsonrpc "8.2.0-next.2" + vscode-languageserver-types "3.17.4-next.2" vscode-languageserver-textdocument@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz#9eae94509cbd945ea44bca8dcfe4bb0c15bb3ac0" integrity sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q== -vscode-languageserver-types@3.17.4-next.0: - version "3.17.4-next.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.0.tgz#4b5238d21cceaeb836d36a05d23c61a8c0238de2" - integrity sha512-2FPKboHnT04xYjfM8JpJVBz4a/tryMw58jmzucaabZMZN5hzoFBrhc97jNG4n6edr9JUb9+QSwwcAcYpDTAoag== +vscode-languageserver-types@3.17.4-next.2: + version "3.17.4-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.2.tgz#4099ff39b38edbd2680df13bfb1c05f0c07bfe8d" + integrity sha512-r6tXyCXyXQH7b6VHkvRT0Nd9v+DWQiosgTR6HQajCb4iJ1myr3KgueWEGBF1Ph5/YAiDy8kXUhf8dHl7wE1H2A== vscode-languageserver-types@^3.17.3: version "3.17.3" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz#72d05e47b73be93acb84d6e311b5786390f13f64" integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA== -vscode-languageserver@^8.2.0-next.1: - version "8.2.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.2.0-next.1.tgz#ad2558d74392b1cfaccd427febe9a368fc328f8b" - integrity sha512-994AXMKBijzjlnpf8p9M+ntsNJDjR8pr55NJPYxKjy/nUhVkg962dAomelH6Z94401kBZmSbfP/K/20cB54aFA== +vscode-languageserver@^8.2.0-next.3: + version "8.2.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.2.0-next.3.tgz#72e4998392260173fb0c35d2d556fb4015f56ce3" + integrity sha512-fqHRwcIRoxfKke7iLDSeUmdo3uk7o/uWNn/44xdWa4urdhsvpTZ5c1GsL1EX4TAvdDg0qeXy89NBZ5Gld2DkgQ== dependencies: - vscode-languageserver-protocol "3.17.4-next.1" + vscode-languageserver-protocol "3.17.4-next.3" vscode-uri@^3.0.7: version "3.0.7" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index 8bf14ed8f82..57d4562ac85 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,39 +185,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -170,15 +264,30 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/extension-telemetry@^0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -187,22 +296,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -236,6 +347,11 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -260,7 +376,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -272,17 +388,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -300,6 +416,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -317,6 +445,23 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -343,24 +488,52 @@ minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^7.3.7: +semver@^7.3.7, semver@^7.5.1, semver@^7.5.3: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -shimmer@^1.1.0, shimmer@^1.2.0: +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -370,6 +543,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tslib@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -380,32 +558,32 @@ uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -vscode-jsonrpc@8.2.0-next.0: - version "8.2.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.0.tgz#41409413c8cebf10f2f1b7cc87e330f0e292814c" - integrity sha512-13jYzaFQpTz5qQ2P+l5c/iTVsj1wUpflP0CR/v4XaEpM0oToLEXZBTcuuox1WaGIbu3Av3xxmGNU4Hydl1iNKg== +vscode-jsonrpc@8.2.0-next.2: + version "8.2.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.2.tgz#09d72832353fc7fb43b33c9c68b083907f6a8a68" + integrity sha512-1FQrqLselaLLe5ApFSU/8qGUbJ8tByWbqczMkT2PEDpDYthCQTe5wONPuVphe7BB+FvZwvBFI2kFkY7FtyHc1A== -vscode-languageclient@^8.2.0-next.1: - version "8.2.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.2.0-next.1.tgz#a3f98b80cfa3225fde0583aa6a5c9b20219fa37e" - integrity sha512-oITaqHQ10PM3zXCUu/104wriMeDutXMkQXMaRBWh1jKihcNcUBLC/os7RhqiVGypY0nl+F0pwStAf4Koc8inaw== +vscode-languageclient@^8.2.0-next.3: + version "8.2.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.2.0-next.3.tgz#a5086f451a679ce77106d8fd1e05c8cbf8e9b886" + integrity sha512-Ojo6L2cb7GSiyD864k8vGb9fHxBdZeciHQQOF595C3IDHWg0w4KQ7iN7qGWVdl4wDNwlGTX3wWZawGfPTxnrPQ== dependencies: minimatch "^5.1.0" semver "^7.3.7" - vscode-languageserver-protocol "3.17.4-next.1" + vscode-languageserver-protocol "3.17.4-next.3" -vscode-languageserver-protocol@3.17.4-next.1: - version "3.17.4-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.1.tgz#a15480e1bc663853ae90ded226efafc5ab333616" - integrity sha512-qrK4BycgPR/+nkRN9PRVTblkLp+kUPUmAgF6rDhFzZIPXW4/MqWwFUT8uswIMGdlTPPgCEkFO/AYEZK1fDXODg== +vscode-languageserver-protocol@3.17.4-next.3: + version "3.17.4-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.3.tgz#7d1d4fcaaa3213a8f2b8a6f1efa8187163251b7c" + integrity sha512-GnW3ldfzlsDK9B1/L1edBW1ddSakC59r+DRipTYCcXIT/zCCbLID998Dxn+exgrL33e3/XLQ+7hQQiSz6TnhKQ== dependencies: - vscode-jsonrpc "8.2.0-next.0" - vscode-languageserver-types "3.17.4-next.0" + vscode-jsonrpc "8.2.0-next.2" + vscode-languageserver-types "3.17.4-next.2" -vscode-languageserver-types@3.17.4-next.0: - version "3.17.4-next.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.0.tgz#4b5238d21cceaeb836d36a05d23c61a8c0238de2" - integrity sha512-2FPKboHnT04xYjfM8JpJVBz4a/tryMw58jmzucaabZMZN5hzoFBrhc97jNG4n6edr9JUb9+QSwwcAcYpDTAoag== +vscode-languageserver-types@3.17.4-next.2: + version "3.17.4-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.2.tgz#4099ff39b38edbd2680df13bfb1c05f0c07bfe8d" + integrity sha512-r6tXyCXyXQH7b6VHkvRT0Nd9v+DWQiosgTR6HQajCb4iJ1myr3KgueWEGBF1Ph5/YAiDy8kXUhf8dHl7wE1H2A== vscode-uri@^3.0.7: version "3.0.7" diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 05075ec1389..cd9e69d69b4 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -164,9 +164,9 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.7.5", + "@vscode/extension-telemetry": "^0.8.4", "request-light": "^0.7.0", - "vscode-languageclient": "^8.2.0-next.1" + "vscode-languageclient": "^8.2.0-next.3" }, "devDependencies": { "@types/node": "18.x" diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index b0012a72816..8d100ef9087 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -12,11 +12,11 @@ }, "main": "./out/node/jsonServerMain", "dependencies": { - "@vscode/l10n": "^0.0.14", + "@vscode/l10n": "^0.0.16", "jsonc-parser": "^3.2.0", "request-light": "^0.7.0", - "vscode-json-languageservice": "^5.3.5", - "vscode-languageserver": "^8.2.0-next.1", + "vscode-json-languageservice": "^5.3.6", + "vscode-languageserver": "^8.2.0-next.3", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 48f16a0f42f..c433012b1a3 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -12,15 +12,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/l10n@^0.0.13": - version "0.0.13" - resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.13.tgz#f51ff130b8c98f189476c5f812d214b8efb09590" - integrity sha512-A3uY356uOU9nGa+TQIT/i3ziWUgJjVMUrGGXSrtRiTwklyCFjGVWIOHoEIHbJpiyhDkJd9kvIWUOfXK1IkK8XQ== - -"@vscode/l10n@^0.0.14": - version "0.0.14" - resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.14.tgz#431e5814c35c3cb11ee21873bc70a4b0fbf90fcf" - integrity sha512-/yrv59IEnmh655z1oeDnGcvMYwnEzNzHLgeYcQCkhYX0xBvYWrAuefoiLcPBUkMpJsb46bqQ6Yv4pwTTQ4d3Qg== +"@vscode/l10n@^0.0.16": + version "0.0.16" + resolved "https://registry.yarnpkg.com/@vscode/l10n/-/l10n-0.0.16.tgz#f075db346d0b08419a12540171b230bd803c42be" + integrity sha512-JT5CvrIYYCrmB+dCana8sUqJEcGB1ZDXNLMQ2+42bW995WmNoenijWMUdZfwmuQUTQcEVVIa2OecZzTYWUW9Cg== jsonc-parser@^3.2.0: version "3.2.0" @@ -32,51 +27,51 @@ request-light@^0.7.0: resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.7.0.tgz#885628bb2f8040c26401ebf258ec51c4ae98ac2a" integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== -vscode-json-languageservice@^5.3.5: - version "5.3.5" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.3.5.tgz#20acd827e13ea4bdeb9976df84ec2bfbb2452c73" - integrity sha512-DasT+bKtpaS2rTPEB4VMROnvO1WES2KD8RZZxXbumnk9sk5wco10VdB6sJgTlsKQN14tHQLZDXuHnSoSAlE8LQ== +vscode-json-languageservice@^5.3.6: + version "5.3.6" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.3.6.tgz#8cbe39dfdf29e7f7e97c9b6966b76031991290f6" + integrity sha512-P4kthBi3GMLKi7Lmp24nkKHAWxbFfCsIDBPlMrK1Tag1aqbl3l60UferDkfAasupDVBM2dekbArzGycUjw8OHA== dependencies: - "@vscode/l10n" "^0.0.13" + "@vscode/l10n" "^0.0.16" jsonc-parser "^3.2.0" vscode-languageserver-textdocument "^1.0.8" vscode-languageserver-types "^3.17.3" vscode-uri "^3.0.7" -vscode-jsonrpc@8.2.0-next.0: - version "8.2.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.0.tgz#41409413c8cebf10f2f1b7cc87e330f0e292814c" - integrity sha512-13jYzaFQpTz5qQ2P+l5c/iTVsj1wUpflP0CR/v4XaEpM0oToLEXZBTcuuox1WaGIbu3Av3xxmGNU4Hydl1iNKg== +vscode-jsonrpc@8.2.0-next.2: + version "8.2.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.2.tgz#09d72832353fc7fb43b33c9c68b083907f6a8a68" + integrity sha512-1FQrqLselaLLe5ApFSU/8qGUbJ8tByWbqczMkT2PEDpDYthCQTe5wONPuVphe7BB+FvZwvBFI2kFkY7FtyHc1A== -vscode-languageserver-protocol@3.17.4-next.1: - version "3.17.4-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.1.tgz#a15480e1bc663853ae90ded226efafc5ab333616" - integrity sha512-qrK4BycgPR/+nkRN9PRVTblkLp+kUPUmAgF6rDhFzZIPXW4/MqWwFUT8uswIMGdlTPPgCEkFO/AYEZK1fDXODg== +vscode-languageserver-protocol@3.17.4-next.3: + version "3.17.4-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.3.tgz#7d1d4fcaaa3213a8f2b8a6f1efa8187163251b7c" + integrity sha512-GnW3ldfzlsDK9B1/L1edBW1ddSakC59r+DRipTYCcXIT/zCCbLID998Dxn+exgrL33e3/XLQ+7hQQiSz6TnhKQ== dependencies: - vscode-jsonrpc "8.2.0-next.0" - vscode-languageserver-types "3.17.4-next.0" + vscode-jsonrpc "8.2.0-next.2" + vscode-languageserver-types "3.17.4-next.2" vscode-languageserver-textdocument@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.8.tgz#9eae94509cbd945ea44bca8dcfe4bb0c15bb3ac0" integrity sha512-1bonkGqQs5/fxGT5UchTgjGVnfysL0O8v1AYMBjqTbWQTFn721zaPGDYFkOKtfDgFiSgXM3KwaG3FMGfW4Ed9Q== -vscode-languageserver-types@3.17.4-next.0: - version "3.17.4-next.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.0.tgz#4b5238d21cceaeb836d36a05d23c61a8c0238de2" - integrity sha512-2FPKboHnT04xYjfM8JpJVBz4a/tryMw58jmzucaabZMZN5hzoFBrhc97jNG4n6edr9JUb9+QSwwcAcYpDTAoag== +vscode-languageserver-types@3.17.4-next.2: + version "3.17.4-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.2.tgz#4099ff39b38edbd2680df13bfb1c05f0c07bfe8d" + integrity sha512-r6tXyCXyXQH7b6VHkvRT0Nd9v+DWQiosgTR6HQajCb4iJ1myr3KgueWEGBF1Ph5/YAiDy8kXUhf8dHl7wE1H2A== vscode-languageserver-types@^3.17.3: version "3.17.3" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz#72d05e47b73be93acb84d6e311b5786390f13f64" integrity sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA== -vscode-languageserver@^8.2.0-next.1: - version "8.2.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.2.0-next.1.tgz#ad2558d74392b1cfaccd427febe9a368fc328f8b" - integrity sha512-994AXMKBijzjlnpf8p9M+ntsNJDjR8pr55NJPYxKjy/nUhVkg962dAomelH6Z94401kBZmSbfP/K/20cB54aFA== +vscode-languageserver@^8.2.0-next.3: + version "8.2.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-8.2.0-next.3.tgz#72e4998392260173fb0c35d2d556fb4015f56ce3" + integrity sha512-fqHRwcIRoxfKke7iLDSeUmdo3uk7o/uWNn/44xdWa4urdhsvpTZ5c1GsL1EX4TAvdDg0qeXy89NBZ5Gld2DkgQ== dependencies: - vscode-languageserver-protocol "3.17.4-next.1" + vscode-languageserver-protocol "3.17.4-next.3" vscode-uri@^3.0.7: version "3.0.7" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 98083a9489f..1ea64055bdd 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -17,7 +17,16 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" -"@azure/core-rest-pipeline@^1.10.0": +"@azure/core-auth@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.5.0.tgz#a41848c5c31cb3b7c84c409885267d55a2c92e44" + integrity sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-util" "^1.1.0" + tslib "^2.2.0" + +"@azure/core-rest-pipeline@1.10.1": version "1.10.1" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.10.1.tgz#348290847ca31b9eecf9cf5de7519aaccdd30968" integrity sha512-Kji9k6TOFRDB5ZMTw8qUf2IJ+CeJtsuMdAHox9eqpTf1cefiNMpzrfnF6sINEBZJsaVaWgQ0o48B6kcUH68niA== @@ -33,13 +42,21 @@ tslib "^2.2.0" uuid "^8.3.0" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.0.1.tgz#352a38cbea438c4a83c86b314f48017d70ba9503" integrity sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw== dependencies: tslib "^2.2.0" +"@azure/core-util@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.2.0.tgz#3499deba1fc36dda6f1912b791809b6f15d4a392" + integrity sha512-ffGIw+Qs8bNKNLxz5UPkz4/VBM/EZY07mPve1ZYFqYUdPwFqRj0RPk0U7LZMOfT7GCck9YjuT1Rfp1PApNl1ng== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/core-util@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.1.1.tgz#8f87b3dd468795df0f0849d9f096c3e7b29452c1" @@ -48,6 +65,14 @@ "@azure/abort-controller" "^1.0.0" tslib "^2.2.0" +"@azure/core-util@^1.1.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.4.0.tgz#c120a56b3e48a9e4d20619a0b00268ae9de891c7" + integrity sha512-eGAyJpm3skVQoLiRqm/xPa+SXi/NPDdSHMxbRAz2lSprd+Zs+qrpQGQQ2VQ3Nttu+nSZR4XoYQC71LbEI7jsig== + dependencies: + "@azure/abort-controller" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0": version "1.0.3" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.0.3.tgz#6e36704aa51be7d4a1bae24731ea580836293c96" @@ -55,66 +80,100 @@ dependencies: tslib "^2.2.0" -"@microsoft/1ds-core-js@3.2.8", "@microsoft/1ds-core-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.8.tgz#1b6b7d9bb858238c818ccf4e4b58ece7aeae5760" - integrity sha512-9o9SUAamJiTXIYwpkQDuueYt83uZfXp8zp8YFix1IwVPwC9RmE36T2CX9gXOeq1nDckOuOduYpA8qHvdh5BGfQ== +"@azure/opentelemetry-instrumentation-azure-sdk@^1.0.0-beta.5": + version "1.0.0-beta.5" + resolved "https://registry.yarnpkg.com/@azure/opentelemetry-instrumentation-azure-sdk/-/opentelemetry-instrumentation-azure-sdk-1.0.0-beta.5.tgz#78809e6c005d08450701e5d37f087f6fce2f86eb" + integrity sha512-fsUarKQDvjhmBO4nIfaZkfNSApm1hZBzcvpNbSrXdcUBxu7lRvKsV5DnwszX7cnhLyVOW9yl1uigtRQ1yDANjA== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" + "@azure/core-tracing" "^1.0.0" + "@azure/logger" "^1.0.0" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/instrumentation" "^0.41.2" + tslib "^2.2.0" + +"@microsoft/1ds-core-js@3.2.13", "@microsoft/1ds-core-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz#0c105ed75091bae3f1555c0334704fa9911c58fb" + integrity sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg== + dependencies: + "@microsoft/applicationinsights-core-js" "2.8.15" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/1ds-post-js@^3.2.8": - version "3.2.8" - resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.8.tgz#46793842cca161bf7a2a5b6053c349f429e55110" - integrity sha512-SjlRoNcXcXBH6WQD/5SkkaCHIVqldH3gDu+bI7YagrOVJ5APxwT1Duw9gm3L1FjFa9S2i81fvJ3EVSKpp9wULA== +"@microsoft/1ds-post-js@^3.2.13": + version "3.2.13" + resolved "https://registry.yarnpkg.com/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz#560aacac8a92fdbb79e8c2ebcb293d56e19f51aa" + integrity sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA== dependencies: - "@microsoft/1ds-core-js" "3.2.8" + "@microsoft/1ds-core-js" "3.2.13" "@microsoft/applicationinsights-shims" "^2.0.2" "@microsoft/dynamicproto-js" "^1.1.7" -"@microsoft/applicationinsights-channel-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-2.8.9.tgz#840656f3c716de8b3eb0a98c122aa1b92bb8ebfb" - integrity sha512-fMBsAEB7pWtPn43y72q9Xy5E5y55r6gMuDQqRRccccVoQDPXyS57VCj5IdATblctru0C6A8XpL2vRyNmEsu0Vg== +"@microsoft/applicationinsights-channel-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.2.tgz#be49fbf74831c7b8c97950027c5052ea99d2a8a5" + integrity sha512-jDBNKbCHsJgmpv0CKNhJ/uN9ZphvfGdb93Svk+R4LjO8L3apNNMbDDPxBvXXi0uigRmA1TBcmyBG4IRKjabGhw== dependencies: - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-common@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-2.8.9.tgz#a75e4a3143a7fd797687830c0ddd2069fd900827" - integrity sha512-mObn1moElyxZaGIRF/IU3cOaeKMgxghXnYEoHNUCA2e+rNwBIgxjyKkblFIpmGuHf4X7Oz3o3yBWpaC6AoMpig== +"@microsoft/applicationinsights-common@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.2.tgz#37670bb07f4858ed41ff9759119e0759007d6e05" + integrity sha512-y+WXWop+OVim954Cu1uyYMnNx6PWO8okHpZIQi/1YSqtqaYdtJVPv4P0AVzwJdohxzVfgzKvqj9nec/VWqE2Zg== dependencies: - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" -"@microsoft/applicationinsights-core-js@2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.9.tgz#0e5d207acfae6986a6fc97249eeb6117e523bf1b" - integrity sha512-HRuIuZ6aOWezcg/G5VyFDDWGL8hDNe/ljPP01J7ImH2kRPEgbtcfPSUMjkamGMefgdq81GZsSoC/NNGTP4pp2w== +"@microsoft/applicationinsights-core-js@2.8.15": + version "2.8.15" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz#8fa466474260e01967fe649f14dd9e5ff91dcdc8" + integrity sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ== dependencies: "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@microsoft/dynamicproto-js" "^1.1.9" + +"@microsoft/applicationinsights-core-js@3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.2.tgz#108e20df8c162bec92b1f66f9de2530a25d9f51a" + integrity sha512-WQhVhzlRlLDrQzn3OShCW/pL3BW5WC57t0oywSknX3q7lMzI3jDg7Ihh0iuIcNTzGCTbDkuqr4d6IjEDWIMtJQ== + dependencies: + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-shims@2.0.2", "@microsoft/applicationinsights-shims@^2.0.2": version "2.0.2" resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz#92b36a09375e2d9cb2b4203383b05772be837085" integrity sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg== -"@microsoft/applicationinsights-web-basic@^2.8.9": - version "2.8.9" - resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-2.8.9.tgz#eed2f3d1e19069962ed2155915c1656e6936e1d5" - integrity sha512-CH0J8JFOy7MjK8JO4pXXU+EML+Ilix+94PMZTX5EJlBU1in+mrik74/8qSg3UC4ekPi12KwrXaHCQSVC3WseXQ== +"@microsoft/applicationinsights-shims@3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz#3865b73ace8405b9c4618cc5c571f2fe3876f06f" + integrity sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg== dependencies: - "@microsoft/applicationinsights-channel-js" "2.8.9" - "@microsoft/applicationinsights-common" "2.8.9" - "@microsoft/applicationinsights-core-js" "2.8.9" - "@microsoft/applicationinsights-shims" "2.0.2" - "@microsoft/dynamicproto-js" "^1.1.7" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" + +"@microsoft/applicationinsights-web-basic@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.2.tgz#f777a4d24b79dde3ae396d3b819e1fce06b7240a" + integrity sha512-6Lq0DE/pZp9RvSV+weGbcxN1NDmfczj6gNPhvZKV2YSQ3RK0LZE3+wjTWLXfuStq8a+nCBdsRpWk8tOKgsoxcg== + dependencies: + "@microsoft/applicationinsights-channel-js" "3.0.2" + "@microsoft/applicationinsights-common" "3.0.2" + "@microsoft/applicationinsights-core-js" "3.0.2" + "@microsoft/applicationinsights-shims" "3.0.1" + "@microsoft/dynamicproto-js" "^2.0.2" + "@nevware21/ts-async" ">= 0.2.4 < 2.x" + "@nevware21/ts-utils" ">= 0.9.5 < 2.x" "@microsoft/applicationinsights-web-snippet@^1.0.1": version "1.0.1" @@ -126,39 +185,74 @@ resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.7.tgz#ede48dd3f85af14ee369c805e5ed5b84222b9fe2" integrity sha512-SK3D3aVt+5vOOccKPnGaJWB5gQ8FuKfjboUJHedMP7gu54HqSCXX5iFXhktGD8nfJb0Go30eDvs/UDoTnR2kOA== -"@opentelemetry/api@^1.0.4": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.2.0.tgz#89ef99401cde6208cff98760b67663726ef26686" - integrity sha512-0nBr+VZNKm9tvNDZFstI3Pq1fCTEDK5OZTnVKNvBNAKgd0yIvmwsP4m61rEv7ZP+tOUjWJhROpxK5MsnlF911g== +"@microsoft/dynamicproto-js@^1.1.9": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz#7437db7aa061162ee94e4131b69a62b8dad5dea6" + integrity sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ== -"@opentelemetry/core@1.7.0", "@opentelemetry/core@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.7.0.tgz#83bdd1b7a4ceafcdffd6590420657caec5f7b34c" - integrity sha512-AVqAi5uc8DrKJBimCTFUT4iFI+5eXpo4sYmGbQ0CypG0piOTHE2g9c5aSoTGYXu3CzOmJZf7pT6Xh+nwm5d6yQ== +"@microsoft/dynamicproto-js@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz#e57fbec2e7067d48b7e8e1e1c1d354028ef718a6" + integrity sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg== dependencies: - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.9.4 < 2.x" -"@opentelemetry/resources@1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.7.0.tgz#90ccd3a6a86b4dfba4e833e73944bd64958d78c5" - integrity sha512-u1M0yZotkjyKx8dj+46Sg5thwtOTBmtRieNXqdCRiWUp6SfFiIP0bI+1XK3LhuXqXkBXA1awJZaTqKduNMStRg== +"@nevware21/ts-async@>= 0.2.4 < 2.x": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@nevware21/ts-async/-/ts-async-0.3.0.tgz#a8b97ba01065fc930de9a3f4dd4a05e862becc6c" + integrity sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@nevware21/ts-utils" ">= 0.10.0 < 2.x" -"@opentelemetry/sdk-trace-base@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.7.0.tgz#b498424e0c6340a9d80de63fd408c5c2130a60a5" - integrity sha512-Iz84C+FVOskmauh9FNnj4+VrA+hG5o+tkMzXuoesvSfunVSioXib0syVFeNXwOm4+M5GdWCuW632LVjqEXStIg== +"@nevware21/ts-utils@>= 0.10.0 < 2.x", "@nevware21/ts-utils@>= 0.9.4 < 2.x", "@nevware21/ts-utils@>= 0.9.5 < 2.x": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz#aa65abc71eba06749a396598f22263d26f796ac7" + integrity sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg== + +"@opentelemetry/api@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" + integrity sha512-O2yRJce1GOc6PAy3QxFM4NzFiWzvScDC1/5ihYBL6BUEVdq0XMWN01sppE+H6bBXbaFYipjwFLEWLg5PaSOThA== + +"@opentelemetry/core@1.15.2", "@opentelemetry/core@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/core/-/core-1.15.2.tgz#5b170bf223a2333884bbc2d29d95812cdbda7c9f" + integrity sha512-+gBv15ta96WqkHZaPpcDHiaz0utiiHZVfm2YOYSqFGrUaJpPkMoSuLBB58YFQGi6Rsb9EHos84X6X5+9JspmLw== dependencies: - "@opentelemetry/core" "1.7.0" - "@opentelemetry/resources" "1.7.0" - "@opentelemetry/semantic-conventions" "1.7.0" + "@opentelemetry/semantic-conventions" "1.15.2" -"@opentelemetry/semantic-conventions@1.7.0", "@opentelemetry/semantic-conventions@^1.0.1": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.7.0.tgz#af80a1ef7cf110ea3a68242acd95648991bcd763" - integrity sha512-FGBx/Qd09lMaqQcogCHyYrFEpTx4cAjeS+48lMIR12z7LdH+zofGDVQSubN59nL6IpubfKqTeIDu9rNO28iHVA== +"@opentelemetry/instrumentation@^0.41.2": + version "0.41.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.41.2.tgz#cae11fa64485dcf03dae331f35b315b64bc6189f" + integrity sha512-rxU72E0pKNH6ae2w5+xgVYZLzc5mlxAbGzF4shxMVK8YC2QQsfN38B2GPbj0jvrKWWNUElfclQ+YTykkNg/grw== + dependencies: + "@types/shimmer" "^1.0.2" + import-in-the-middle "1.4.2" + require-in-the-middle "^7.1.1" + semver "^7.5.1" + shimmer "^1.2.1" + +"@opentelemetry/resources@1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/resources/-/resources-1.15.2.tgz#0c9e26cb65652a1402834a3c030cce6028d6dd9d" + integrity sha512-xmMRLenT9CXmm5HMbzpZ1hWhaUowQf8UB4jMjFlAxx1QzQcsD3KFNAVX/CAWzFPtllTyTplrA4JrQ7sCH3qmYw== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/sdk-trace-base@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.15.2.tgz#4821f94033c55a6c8bbd35ae387b715b6108517a" + integrity sha512-BEaxGZbWtvnSPchV98qqqqa96AOcb41pjgvhfzDij10tkBhIu9m0Jd6tZ1tJB5ZHfHbTffqYVYE0AOGobec/EQ== + dependencies: + "@opentelemetry/core" "1.15.2" + "@opentelemetry/resources" "1.15.2" + "@opentelemetry/semantic-conventions" "1.15.2" + +"@opentelemetry/semantic-conventions@1.15.2", "@opentelemetry/semantic-conventions@^1.15.2": + version "1.15.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.15.2.tgz#3bafb5de3e20e841dff6cb3c66f4d6e9694c4241" + integrity sha512-CjbOKwk2s+3xPIMcd5UNYQzsf+v94RczbdNix9/kQh38WiQkM90sUOi3if8eyHFgiBjBjhwXrA7W3ydiSQP9mw== "@tootallnate/once@2": version "2.0.0" @@ -170,15 +264,30 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.13.tgz#f64277c341150c979e42b00e4ac289290c9df469" integrity sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q== -"@vscode/extension-telemetry@^0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.7.5.tgz#bf965731816e08c3f146f96d901ec67954fc913b" - integrity sha512-fJ5y3TcpqqkFYHneabYaoB4XAhDdVflVm+TDKshw9VOs77jkgNS4UA7LNXrWeO0eDne3Sh3JgURf+xzc1rk69w== +"@types/shimmer@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@types/shimmer/-/shimmer-1.0.2.tgz#93eb2c243c351f3f17d5c580c7467ae5d686b65f" + integrity sha512-dKkr1bTxbEsFlh2ARpKzcaAmsYixqt9UyCdoEZk8rHyE4iQYcDCyvSjDSf7JUWJHlJiTtbIoQjxKh6ViywqDAg== + +"@vscode/extension-telemetry@^0.8.4": + version "0.8.4" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.8.4.tgz#c078c6f55df1c9e0592de3b4ce0f685dd345bfe7" + integrity sha512-UqM9+KZDDK3MyoHTsg6XNM+XO6pweQxzCpqJz33BoBEYAGsbBviRYcVpJglgay2oReuDD2pOI1Nio3BKNDLhWA== dependencies: - "@microsoft/1ds-core-js" "^3.2.8" - "@microsoft/1ds-post-js" "^3.2.8" - "@microsoft/applicationinsights-web-basic" "^2.8.9" - applicationinsights "2.4.1" + "@microsoft/1ds-core-js" "^3.2.13" + "@microsoft/1ds-post-js" "^3.2.13" + "@microsoft/applicationinsights-web-basic" "^3.0.2" + applicationinsights "^2.7.1" + +acorn-import-assertions@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.8.2: + version "8.10.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== agent-base@6: version "6.0.2" @@ -187,22 +296,24 @@ agent-base@6: dependencies: debug "4" -applicationinsights@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.4.1.tgz#4de4c4dd3c7c4a44445cfbf3d15808fc0dcc423d" - integrity sha512-0n0Ikd0gzSm460xm+M0UTWIwXrhrH/0bqfZatcJjYObWyefxfAxapGEyNnSGd1Tg90neHz+Yhf+Ff/zgvPiQYA== +applicationinsights@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-2.7.3.tgz#8781454d29c0b14c9773f2e892b4cf5e7468ffa5" + integrity sha512-JY8+kTEkjbA+kAVNWDtpfW2lqsrDALfDXuxOs74KLPu2y13fy/9WB52V4LfYVTVcW1/jYOXjTxNS2gPZIDh1iw== dependencies: - "@azure/core-auth" "^1.4.0" - "@azure/core-rest-pipeline" "^1.10.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-rest-pipeline" "1.10.1" + "@azure/core-util" "1.2.0" + "@azure/opentelemetry-instrumentation-azure-sdk" "^1.0.0-beta.5" "@microsoft/applicationinsights-web-snippet" "^1.0.1" - "@opentelemetry/api" "^1.0.4" - "@opentelemetry/core" "^1.0.1" - "@opentelemetry/sdk-trace-base" "^1.0.1" - "@opentelemetry/semantic-conventions" "^1.0.1" + "@opentelemetry/api" "^1.4.1" + "@opentelemetry/core" "^1.15.2" + "@opentelemetry/sdk-trace-base" "^1.15.2" + "@opentelemetry/semantic-conventions" "^1.15.2" cls-hooked "^4.2.2" continuation-local-storage "^3.2.1" - diagnostic-channel "1.1.0" - diagnostic-channel-publishers "1.0.5" + diagnostic-channel "1.1.1" + diagnostic-channel-publishers "1.0.7" async-hook-jl@^1.7.6: version "1.7.6" @@ -236,6 +347,11 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +cjs-module-lexer@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz#6c370ab19f8a3394e318fe682686ec0ac684d107" + integrity sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ== + cls-hooked@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" @@ -260,7 +376,7 @@ continuation-local-storage@^3.2.1: async-listener "^0.6.0" emitter-listener "^1.1.1" -debug@4: +debug@4, debug@^4.1.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -272,17 +388,17 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -diagnostic-channel-publishers@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.5.tgz#df8c317086c50f5727fdfb5d2fce214d2e4130ae" - integrity sha512-dJwUS0915pkjjimPJVDnS/QQHsH0aOYhnZsLJdnZIMOrB+csj8RnZhWTuwnm8R5v3Z7OZs+ksv5luC14DGB7eg== +diagnostic-channel-publishers@1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz#9b7f8d5ee1295481aee19c827d917e96fedf2c4a" + integrity sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg== -diagnostic-channel@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.0.tgz#6985e9dfedfbc072d91dc4388477e4087147756e" - integrity sha512-fwujyMe1gj6rk6dYi9hMZm0c8Mz8NDMVl2LB4iaYh3+LIAThZC8RKFGXWG0IML2OxAit/ZFRgZhMkhQ3d/bobQ== +diagnostic-channel@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-1.1.1.tgz#44b60972de9ee055c16216535b0e9db3f6a0efd0" + integrity sha512-r2HV5qFkUICyoaKlBEpLKHjxMXATUf/l+h8UZPGBHGLy4DDiY2sOLcIctax4eRnTw5wH2jTMExLntGPJ8eOJxw== dependencies: - semver "^5.3.0" + semver "^7.5.3" emitter-listener@^1.0.1, emitter-listener@^1.1.1: version "1.1.2" @@ -300,6 +416,18 @@ form-data@^4.0.0: combined-stream "^1.0.8" mime-types "^2.1.12" +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + http-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" @@ -317,6 +445,23 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" +import-in-the-middle@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz#2a266676e3495e72c04bbaa5ec14756ba168391b" + integrity sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw== + dependencies: + acorn "^8.8.2" + acorn-import-assertions "^1.9.0" + cjs-module-lexer "^1.2.2" + module-details-from-path "^1.0.3" + +is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -343,29 +488,57 @@ minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" +module-details-from-path@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + request-light@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.7.0.tgz#885628bb2f8040c26401ebf258ec51c4ae98ac2a" integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== +require-in-the-middle@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-7.2.0.tgz#b539de8f00955444dc8aed95e17c69b0a4f10fcf" + integrity sha512-3TLx5TGyAY6AOqLBoXmHkNql0HIf2RGbuMgCDT2WO/uGVAPJs6h7Kl+bN6TIZGd9bWhWPwnDnTHGtW8Iu77sdw== + dependencies: + debug "^4.1.1" + module-details-from-path "^1.0.3" + resolve "^1.22.1" + +resolve@^1.22.1: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + semver@^5.3.0, semver@^5.4.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^7.3.7: +semver@^7.3.7, semver@^7.5.1, semver@^7.5.3: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -shimmer@^1.1.0, shimmer@^1.2.0: +shimmer@^1.1.0, shimmer@^1.2.0, shimmer@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -375,6 +548,11 @@ stack-chain@^1.3.7: resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" integrity sha512-D8cWtWVdIe/jBA7v5p5Hwl5yOSOrmZPWDPe2KxQ5UAGD+nxbxU0lKXA4h85Ta6+qgdKVL3vUxsbIZjc1kBG7ug== +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + tslib@^2.2.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" @@ -385,32 +563,32 @@ uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -vscode-jsonrpc@8.2.0-next.0: - version "8.2.0-next.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.0.tgz#41409413c8cebf10f2f1b7cc87e330f0e292814c" - integrity sha512-13jYzaFQpTz5qQ2P+l5c/iTVsj1wUpflP0CR/v4XaEpM0oToLEXZBTcuuox1WaGIbu3Av3xxmGNU4Hydl1iNKg== +vscode-jsonrpc@8.2.0-next.2: + version "8.2.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0-next.2.tgz#09d72832353fc7fb43b33c9c68b083907f6a8a68" + integrity sha512-1FQrqLselaLLe5ApFSU/8qGUbJ8tByWbqczMkT2PEDpDYthCQTe5wONPuVphe7BB+FvZwvBFI2kFkY7FtyHc1A== -vscode-languageclient@^8.2.0-next.1: - version "8.2.0-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.2.0-next.1.tgz#a3f98b80cfa3225fde0583aa6a5c9b20219fa37e" - integrity sha512-oITaqHQ10PM3zXCUu/104wriMeDutXMkQXMaRBWh1jKihcNcUBLC/os7RhqiVGypY0nl+F0pwStAf4Koc8inaw== +vscode-languageclient@^8.2.0-next.3: + version "8.2.0-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-8.2.0-next.3.tgz#a5086f451a679ce77106d8fd1e05c8cbf8e9b886" + integrity sha512-Ojo6L2cb7GSiyD864k8vGb9fHxBdZeciHQQOF595C3IDHWg0w4KQ7iN7qGWVdl4wDNwlGTX3wWZawGfPTxnrPQ== dependencies: minimatch "^5.1.0" semver "^7.3.7" - vscode-languageserver-protocol "3.17.4-next.1" + vscode-languageserver-protocol "3.17.4-next.3" -vscode-languageserver-protocol@3.17.4-next.1: - version "3.17.4-next.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.1.tgz#a15480e1bc663853ae90ded226efafc5ab333616" - integrity sha512-qrK4BycgPR/+nkRN9PRVTblkLp+kUPUmAgF6rDhFzZIPXW4/MqWwFUT8uswIMGdlTPPgCEkFO/AYEZK1fDXODg== +vscode-languageserver-protocol@3.17.4-next.3: + version "3.17.4-next.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.4-next.3.tgz#7d1d4fcaaa3213a8f2b8a6f1efa8187163251b7c" + integrity sha512-GnW3ldfzlsDK9B1/L1edBW1ddSakC59r+DRipTYCcXIT/zCCbLID998Dxn+exgrL33e3/XLQ+7hQQiSz6TnhKQ== dependencies: - vscode-jsonrpc "8.2.0-next.0" - vscode-languageserver-types "3.17.4-next.0" + vscode-jsonrpc "8.2.0-next.2" + vscode-languageserver-types "3.17.4-next.2" -vscode-languageserver-types@3.17.4-next.0: - version "3.17.4-next.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.0.tgz#4b5238d21cceaeb836d36a05d23c61a8c0238de2" - integrity sha512-2FPKboHnT04xYjfM8JpJVBz4a/tryMw58jmzucaabZMZN5hzoFBrhc97jNG4n6edr9JUb9+QSwwcAcYpDTAoag== +vscode-languageserver-types@3.17.4-next.2: + version "3.17.4-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.4-next.2.tgz#4099ff39b38edbd2680df13bfb1c05f0c07bfe8d" + integrity sha512-r6tXyCXyXQH7b6VHkvRT0Nd9v+DWQiosgTR6HQajCb4iJ1myr3KgueWEGBF1Ph5/YAiDy8kXUhf8dHl7wE1H2A== yallist@^4.0.0: version "4.0.0" From 9bfbb69c30ceb61d7e96bc4c16712a284038099b Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 28 Aug 2023 14:57:24 -0700 Subject: [PATCH 293/607] Add unit testing for nb sticky scroll (#191524) * static computeContent for sticky testing * nb sticky refactor for testing * move helper fxns to test suite --- .../viewParts/notebookEditorStickyScroll.ts | 290 +++++++------- ...hould_render_empty___scrollTop_at_0_0.snap | 1 + ...ld_render_0-_1___visible_range_3-_8_0.snap | 1 + ...ng_next_2_against_following_section_0.snap | 1 + ...1___collapsing_against_third_header_0.snap | 1 + ...___scrolltop_halfway_through_cell_0_0.snap | 1 + ...___scrolltop_halfway_through_cell_2_0.snap | 1 + ...___scrolltop_halfway_through_cell_7_0.snap | 1 + ...1___collapsing_against_next_section_0.snap | 1 + .../test/browser/notebookStickyScroll.test.ts | 369 ++++++++++++++++++ .../test/browser/testNotebookEditor.ts | 54 ++- 11 files changed, 565 insertions(+), 156 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test0__should_render_empty___scrollTop_at_0_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_third_header_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section_0.snap create mode 100644 src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index 824835d21d0..7b490223afd 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -5,6 +5,7 @@ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; @@ -16,7 +17,6 @@ import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookB import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; import { NotebookCellOutlineProvider, OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider'; import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; export class ToggleNotebookStickyScroll extends Action2 { @@ -48,7 +48,7 @@ export class ToggleNotebookStickyScroll extends Action2 { } } -class NotebookStickyLine extends Disposable { +export class NotebookStickyLine extends Disposable { constructor( public readonly element: HTMLElement, public readonly entry: OutlineEntry, @@ -79,10 +79,9 @@ class NotebookStickyLine extends Disposable { } } - export class NotebookStickyScroll extends Disposable { private readonly _disposables = new DisposableStore(); - private currentStickyLines = new Map(); + private currentStickyLines = new Map(); getDomNode(): HTMLElement { return this.domNode; @@ -92,6 +91,10 @@ export class NotebookStickyScroll extends Disposable { return this.currentStickyLines.size * 22; } + private setCurrentStickyLines(newStickyLines: Map) { + this.currentStickyLines = newStickyLines; + } + constructor( private readonly domNode: HTMLElement, private readonly notebookEditor: INotebookEditor, @@ -132,9 +135,7 @@ export class NotebookStickyScroll extends Disposable { this.init(); } else { this._disposables.clear(); - this.currentStickyLines.forEach((value) => { - value.dispose(); - }); + this.disposeCurrentStickyLines(); DOM.clearNode(this.domNode); this.updateDisplay(); } @@ -153,7 +154,9 @@ export class NotebookStickyScroll extends Disposable { this.initializeContent(); this._disposables.add(this.notebookOutline.onDidChange(() => { - this.updateContent(); + DOM.clearNode(this.domNode); + this.disposeCurrentStickyLines(); + this.updateContent(computeContent(this.domNode, this.notebookEditor, this.notebookCellList, this.notebookOutline.entries)); })); this._disposables.add(this.notebookEditor.onDidAttachViewModel(() => { @@ -162,18 +165,20 @@ export class NotebookStickyScroll extends Disposable { })); this._disposables.add(this.notebookEditor.onDidScroll(() => { - this.updateContent(); + DOM.clearNode(this.domNode); + this.disposeCurrentStickyLines(); + this.updateContent(computeContent(this.domNode, this.notebookEditor, this.notebookCellList, this.notebookOutline.entries)); })); } - private getVisibleOutlineEntry(visibleIndex: number): OutlineEntry | undefined { + static getVisibleOutlineEntry(visibleIndex: number, notebookOutlineEntries: OutlineEntry[]): OutlineEntry | undefined { let left = 0; - let right = this.notebookOutline.entries.length - 1; + let right = notebookOutlineEntries.length - 1; let bucket = -1; while (left <= right) { const mid = Math.floor((left + right) / 2); - if (this.notebookOutline.entries[mid].index < visibleIndex) { + if (notebookOutlineEntries[mid].index < visibleIndex) { bucket = mid; left = mid + 1; } else { @@ -182,7 +187,7 @@ export class NotebookStickyScroll extends Disposable { } if (bucket !== -1) { - const rootEntry = this.notebookOutline.entries[bucket]; + const rootEntry = notebookOutlineEntries[bucket]; const flatList: OutlineEntry[] = []; rootEntry.asFlatList(flatList); return flatList.find(entry => entry.index === visibleIndex); @@ -206,7 +211,7 @@ export class NotebookStickyScroll extends Disposable { for (let i = visibleRange.start; i < visibleRange.end; i++) { if (i === 0) { // don't show headers when you're viewing the top cell this.updateDisplay(); - this.currentStickyLines = new Map(); + this.setCurrentStickyLines(new Map()); return; } const cell = this.notebookEditor.cellAt(i); @@ -226,12 +231,12 @@ export class NotebookStickyScroll extends Disposable { // store the bottom scroll position of this cell sectionBottom = this.notebookCellList.getCellViewScrollBottom(cell); // compute sticky scroll height - const entry = this.getVisibleOutlineEntry(i); + const entry = NotebookStickyScroll.getVisibleOutlineEntry(i, this.notebookOutline.entries); if (!entry) { return; } // using 22 instead of stickyscrollheight, as we don't necessarily render each line. 22 starts rendering sticky when we have space for at least 1 of them - const newStickyHeight = this.computeStickyHeight(entry!); + const newStickyHeight = NotebookStickyScroll.computeStickyHeight(entry!); if (editorScrollTop + newStickyHeight < sectionBottom) { trackedEntry = entry; break; @@ -243,7 +248,7 @@ export class NotebookStickyScroll extends Disposable { } else { // there is no next cell, so use the bottom of the editor as the sectionBottom, using scrolltop + height sectionBottom = this.notebookEditor.scrollTop + this.notebookEditor.getLayoutInfo().scrollHeight; - trackedEntry = this.getVisibleOutlineEntry(i); + trackedEntry = NotebookStickyScroll.getVisibleOutlineEntry(i, this.notebookOutline.entries); break; } } // cell loop close @@ -253,130 +258,14 @@ export class NotebookStickyScroll extends Disposable { // compute the space available for sticky lines, and render sticky lines const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); - let newMap: Map | undefined = new Map(); - newMap = this.renderStickyLines(trackedEntry?.parent, this.domNode, linesToRender, newMap); - if (!newMap) { - newMap = new Map(); - } - this.currentStickyLines = newMap; + let newMap: Map = new Map(); + newMap = NotebookStickyScroll.renderStickyLines(trackedEntry?.parent, this.domNode, linesToRender, newMap, this.notebookEditor); + this.setCurrentStickyLines(newMap); this.updateDisplay(); } - - private updateContent() { - // find first code cell in visible range. this marks the start of the first section - // find the last code cell in the first section of the visible range, store the bottom scroll position in a const sectionBottom - // compute sticky scroll height, and check if editorScrolltop + stickyScrollHeight < sectionBottom - // if that condition is true, break out of the loop with that cell as the tracked cell - // if that condition is false, continue to next cell - - DOM.clearNode(this.domNode); - // iterate over current map and dispose each notebookstickyline - this.currentStickyLines.forEach((value) => { - value.dispose(); - }); - - const editorScrollTop = this.notebookEditor.scrollTop; - - // find last code cell of section, store bottom scroll position in sectionBottom - const visibleRange = this.notebookEditor.visibleRanges[0]; - if (!visibleRange) { - this.updateDisplay(); - this.currentStickyLines = new Map(); - return; - } - - let trackedEntry = undefined; - let sectionBottom = 0; - for (let i = visibleRange.start; i < visibleRange.end; i++) { - if (i === 0) { // don't show headers when you're viewing the top cell - this.updateDisplay(); - this.currentStickyLines = new Map(); - return; - } - const cell = this.notebookEditor.cellAt(i); - if (!cell) { - return; - } - if (cell.cellKind === CellKind.Markup) { - continue; - } - - // if we are here, the cell is a code cell. - // check next cell, if markdown, that means this is the end of the section - const nextVisibleCell = this.notebookEditor.cellAt(i + 1); - if (nextVisibleCell && i + 1 < visibleRange.end) { - if (nextVisibleCell.cellKind === CellKind.Markup) { - // this is the end of the section - // store the bottom scroll position of this cell - sectionBottom = this.notebookCellList.getCellViewScrollBottom(cell); - // compute sticky scroll height - const entry = this.getVisibleOutlineEntry(i); - if (!entry) { - return; - } - // check if we can render this section of sticky - const currentSectionStickyHeight = this.computeStickyHeight(entry!); - if (editorScrollTop + currentSectionStickyHeight < sectionBottom) { - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); - let newMap: Map | undefined = new Map(); - newMap = this.renderStickyLines(entry?.parent, this.domNode, linesToRender, newMap); - if (!newMap) { - newMap = new Map(); - } - this.currentStickyLines = newMap; - break; - } - - let nextSectionEntry = undefined; - for (let j = 1; j < visibleRange.end - i; j++) { - // find next code cell after this one - const cellCheck = this.notebookEditor.cellAt(i + j); - if (cellCheck && cellCheck.cellKind === CellKind.Code) { - nextSectionEntry = this.getVisibleOutlineEntry(i + j); - break; - } - } - const nextSectionStickyHeight = this.computeStickyHeight(nextSectionEntry!); - - // this block of logic cleans transitions between two sections that share a parent. - // if the current section and the next section share a parent, then we can render the next section's sticky lines to avoid pop-in between - if (entry?.parent?.parent === nextSectionEntry?.parent) { - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22) + 1; - let newMap: Map | undefined = new Map(); - newMap = this.renderStickyLines(nextSectionEntry?.parent, this.domNode, linesToRender, newMap); - if (!newMap) { - newMap = new Map(); - } - this.currentStickyLines = newMap; - break; - } else if (Math.abs(currentSectionStickyHeight - nextSectionStickyHeight) > 22) { // only shrink sticky - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); - let newMap: Map | undefined = new Map(); - newMap = this.renderStickyLines(entry?.parent, this.domNode, linesToRender, newMap); - if (!newMap) { - newMap = new Map(); - } - this.currentStickyLines = newMap; - break; - } - } - } else { - // there is no next cell, so use the bottom of the editor as the sectionBottom, using scrolltop + height - sectionBottom = this.notebookEditor.scrollTop + this.notebookEditor.getLayoutInfo().scrollHeight; - trackedEntry = this.getVisibleOutlineEntry(i); - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); - - let newMap: Map | undefined = new Map(); - newMap = this.renderStickyLines(trackedEntry?.parent, this.domNode, linesToRender, newMap); - if (!newMap) { - newMap = new Map(); - } - this.currentStickyLines = newMap; - - break; - } - } // cell loop close + private updateContent(newMap: Map) { + this.setCurrentStickyLines(newMap); this.updateDisplay(); } @@ -390,7 +279,7 @@ export class NotebookStickyScroll extends Disposable { this.setTop(); } - private computeStickyHeight(entry: OutlineEntry) { + static computeStickyHeight(entry: OutlineEntry) { let height = 0; while (entry.parent) { height += 22; @@ -399,19 +288,19 @@ export class NotebookStickyScroll extends Disposable { return height; } - private renderStickyLines(entry: OutlineEntry | undefined, containerElement: HTMLElement, numLinesToRender: number, newMap: Map) { + static renderStickyLines(entry: OutlineEntry | undefined, containerElement: HTMLElement, numLinesToRender: number, newMap: Map, notebookEditor: INotebookEditor) { const partial = false; let currentEntry = entry; const elementsToRender = []; while (currentEntry) { if (currentEntry.level === 7) { - // level 7 represents a comment in python, which we don't want to render + // level 7 represents a non-header entry, which we don't want to render currentEntry = currentEntry.parent; continue; } - const lineToRender = this.createStickyElement(currentEntry, partial); - newMap.set(currentEntry, lineToRender); + const lineToRender = NotebookStickyScroll.createStickyElement(currentEntry, partial, notebookEditor); + newMap.set(currentEntry, { line: lineToRender, rendered: false }); elementsToRender.unshift(lineToRender); currentEntry = currentEntry.parent; } @@ -423,33 +312,128 @@ export class NotebookStickyScroll extends Disposable { break; } containerElement.append(elementsToRender[i].element); + newMap.set(elementsToRender[i].entry, { line: elementsToRender[i], rendered: true }); } containerElement.append(DOM.$('div', { class: 'notebook-shadow' })); // ensure we have dropShadow at base of sticky scroll return newMap; } - private createStickyElement(entry: OutlineEntry, partial: boolean) { + static createStickyElement(entry: OutlineEntry, partial: boolean, notebookEditor: INotebookEditor) { const stickyElement = document.createElement('div'); stickyElement.classList.add('notebook-sticky-scroll-line'); stickyElement.innerText = '#'.repeat(entry.level) + ' ' + entry.label; - // todo: partial line rendering for animater - if (partial) { - // const partialHeight = Math.floor(remainder * 22); - // stickyLine.style.height = `${partialHeight}px`; - } + // todo: partial line rendering for animation + // if (partial) { + // const partialHeight = Math.floor(remainder * 22); + // stickyLine.style.height = `${partialHeight}px`; + // } - return new NotebookStickyLine(stickyElement, entry, this.notebookEditor); + return new NotebookStickyLine(stickyElement, entry, notebookEditor); + } + + private disposeCurrentStickyLines() { + this.currentStickyLines.forEach((value) => { + value.line.dispose(); + }); } override dispose() { this._disposables.dispose(); - this.currentStickyLines.forEach((value) => { - value.dispose(); - }); + this.disposeCurrentStickyLines(); super.dispose(); } } +export function computeContent(domNode: HTMLElement, notebookEditor: INotebookEditor, notebookCellList: INotebookCellList, notebookOutlineEntries: OutlineEntry[]): Map { + // find first code cell in visible range. this marks the start of the first section + // find the last code cell in the first section of the visible range, store the bottom scroll position in a const sectionBottom + // compute sticky scroll height, and check if editorScrolltop + stickyScrollHeight < sectionBottom + // if that condition is true, break out of the loop with that cell as the tracked cell + // if that condition is false, continue to next cell + + const editorScrollTop = notebookEditor.scrollTop; + + // find last code cell of section, store bottom scroll position in sectionBottom + const visibleRange = notebookEditor.visibleRanges[0]; + if (!visibleRange) { + return new Map(); + } + + let trackedEntry = undefined; + let sectionBottom = 0; + for (let i = visibleRange.start; i < visibleRange.end; i++) { + if (i === 0) { // don't show headers when you're viewing the top cell + return new Map(); + } + const cell = notebookEditor.cellAt(i); + if (!cell) { + return new Map(); + } + if (cell.cellKind === CellKind.Markup) { + continue; + } + + // if we are here, the cell is a code cell. + // check next cell, if markdown, that means this is the end of the section + const nextVisibleCell = notebookEditor.cellAt(i + 1); + if (nextVisibleCell && i + 1 < visibleRange.end) { + if (nextVisibleCell.cellKind === CellKind.Markup) { + // this is the end of the section + // store the bottom scroll position of this cell + sectionBottom = notebookCellList.getCellViewScrollBottom(cell); + // compute sticky scroll height + const entry = NotebookStickyScroll.getVisibleOutlineEntry(i, notebookOutlineEntries); + if (!entry) { + return new Map(); + } + // check if we can render this section of sticky + const currentSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(entry!); + if (editorScrollTop + currentSectionStickyHeight < sectionBottom) { + const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); + let newMap: Map = new Map(); + newMap = NotebookStickyScroll.renderStickyLines(entry?.parent, domNode, linesToRender, newMap, notebookEditor); + return newMap; + } + + let nextSectionEntry = undefined; + for (let j = 1; j < visibleRange.end - i; j++) { + // find next code cell after this one + const cellCheck = notebookEditor.cellAt(i + j); + if (cellCheck && cellCheck.cellKind === CellKind.Code) { + nextSectionEntry = NotebookStickyScroll.getVisibleOutlineEntry(i + j, notebookOutlineEntries); + break; + } + } + const nextSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(nextSectionEntry!); + + // this block of logic cleans transitions between two sections that share a parent. + // if the current section and the next section share a parent, then we can render the next section's sticky lines to avoid pop-in between + if (entry?.parent?.parent === nextSectionEntry?.parent) { + const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22) + 1; + let newMap: Map = new Map(); + newMap = NotebookStickyScroll.renderStickyLines(nextSectionEntry?.parent, domNode, linesToRender, newMap, notebookEditor); + return newMap; + } else if (Math.abs(currentSectionStickyHeight - nextSectionStickyHeight) > 22) { // only shrink sticky + const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); + let newMap: Map = new Map(); + newMap = NotebookStickyScroll.renderStickyLines(entry?.parent, domNode, linesToRender, newMap, notebookEditor); + return newMap; + } + } + } else { + // there is no next cell, so use the bottom of the editor as the sectionBottom, using scrolltop + height + sectionBottom = notebookEditor.getLayoutInfo().scrollHeight; + trackedEntry = NotebookStickyScroll.getVisibleOutlineEntry(i, notebookOutlineEntries); + const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); + + let newMap: Map = new Map(); + newMap = NotebookStickyScroll.renderStickyLines(trackedEntry?.parent, domNode, linesToRender, newMap, notebookEditor); + return newMap; + } + } // for cell loop close + return new Map(); +} + registerAction2(ToggleNotebookStickyScroll); diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test0__should_render_empty___scrollTop_at_0_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test0__should_render_empty___scrollTop_at_0_0.snap new file mode 100644 index 00000000000..f6ffad5cb32 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test0__should_render_empty___scrollTop_at_0_0.snap @@ -0,0 +1 @@ +[ ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8_0.snap new file mode 100644 index 00000000000..1bcf0a58d43 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test1__should_render_0-_1___visible_range_3-_8_0.snap @@ -0,0 +1 @@ +[ "# header a", "## header aa" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section_0.snap new file mode 100644 index 00000000000..3f23335f240 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test2__should_render_0____visible_range_6-_9_so_collapsing_next_2_against_following_section_0.snap @@ -0,0 +1 @@ +[ "# header a" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_third_header_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_third_header_0.snap new file mode 100644 index 00000000000..1bcf0a58d43 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_third_header_0.snap @@ -0,0 +1 @@ +[ "# header a", "## header aa" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0_0.snap new file mode 100644 index 00000000000..3f23335f240 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test4__should_render_0____scrolltop_halfway_through_cell_0_0.snap @@ -0,0 +1 @@ +[ "# header a" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2_0.snap new file mode 100644 index 00000000000..cf5583b0ab9 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test5__should_render_0-_2___scrolltop_halfway_through_cell_2_0.snap @@ -0,0 +1 @@ +[ "# header a", "## header aa", "### header aaa" ] diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7_0.snap new file mode 100644 index 00000000000..77fe21fe781 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test6__should_render_6-_7___scrolltop_halfway_through_cell_7_0.snap @@ -0,0 +1 @@ +[ "# header b", "## header bb" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section_0.snap new file mode 100644 index 00000000000..1bcf0a58d43 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test7__should_render_0-_1___collapsing_against_next_section_0.snap @@ -0,0 +1 @@ +[ "# header a", "## header aa" ] \ No newline at end of file diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts new file mode 100644 index 00000000000..46bd58ded14 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts @@ -0,0 +1,369 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { Event } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { mock } from 'vs/base/test/common/mock'; +import { assertSnapshot } from 'vs/base/test/common/snapshot'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { NotebookCellOutline } from 'vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline'; +import { INotebookEditor, INotebookEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { OutlineEntry } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider'; +import { NotebookStickyLine, computeContent } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll'; +import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { createNotebookCellList, setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; +import { OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; + + +suite('NotebookEditorStickyScroll', () => { + + let disposables: DisposableStore; + let instantiationService: TestInstantiationService; + + const domNode: HTMLElement = document.createElement('div'); + + suiteSetup(() => { + disposables = new DisposableStore(); + instantiationService = setupInstantiationService(disposables); + }); + + suiteTeardown(() => disposables.dispose()); + + function getOutline(editor: any) { + if (!editor.hasModel()) { + assert.ok(false, 'MUST have active text editor'); + } + const outline = instantiationService.createInstance(NotebookCellOutline, new class extends mock() { + override getControl() { + return editor; + } + override onDidChangeModel: Event = Event.None; + }, OutlineTarget.QuickPick); + return outline; + } + + function nbStickyTestHelper(domNode: HTMLElement, notebookEditor: INotebookEditor, notebookCellList: INotebookCellList, notebookOutlineEntries: OutlineEntry[]) { + const output = computeContent(domNode, notebookEditor, notebookCellList, notebookOutlineEntries); + return createStickyTestElement(output.values()); + } + + function createStickyTestElement(stickyLines: IterableIterator<{ line: NotebookStickyLine; rendered: boolean }>) { + const outputElements = []; + for (const stickyLine of stickyLines) { + if (stickyLine.rendered) { + outputElements.unshift(stickyLine.line.element.innerText); + } + } + return outputElements; + } + + test('test0: should render empty, scrollTop at 0', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], + ['## header aa', 'markdown', CellKind.Markup, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markup, [], {}], + ['var c = 2;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 8 }, () => false), + editorViewStates: Array.from({ length: 8 }, () => null), + cellTotalHeights: Array.from({ length: 8 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(0); + editor.visibleRanges = [{ start: 0, end: 8 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + test('test1: should render 0->1, visible range 3->8', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], // 0 + ['## header aa', 'markdown', CellKind.Markup, [], {}], // 50 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 100 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 200 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250 + ['# header b', 'markdown', CellKind.Markup, [], {}], // 300 + ['var c = 2;', 'javascript', CellKind.Code, [], {}] // 350 + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 8 }, () => false), + editorViewStates: Array.from({ length: 8 }, () => null), + cellTotalHeights: Array.from({ length: 8 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(175); + editor.visibleRanges = [{ start: 3, end: 8 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + test('test2: should render 0, visible range 6->9 so collapsing next 2 against following section', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], // 0 + ['## header aa', 'markdown', CellKind.Markup, [], {}], // 50 + ['### header aaa', 'markdown', CellKind.Markup, [], {}],// 100 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 200 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 300 + ['# header b', 'markdown', CellKind.Markup, [], {}], // 350 + ['var c = 2;', 'javascript', CellKind.Code, [], {}] // 400 + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 9 }, () => false), + editorViewStates: Array.from({ length: 9 }, () => null), + cellTotalHeights: Array.from({ length: 9 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(325); // room for a single header + editor.visibleRanges = [{ start: 6, end: 9 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + // waiting on behavior push to fix this. + test.skip('test3: should render 0->1, collapsing against equivalent level header', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], // 0 + ['## header aa', 'markdown', CellKind.Markup, [], {}], // 50 + ['### header aaa', 'markdown', CellKind.Markup, [], {}],// 100 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150 + ['### header aab', 'markdown', CellKind.Markup, [], {}],// 200 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 300 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], // 350 + ['# header b', 'markdown', CellKind.Markup, [], {}], // 400 + ['var c = 2;', 'javascript', CellKind.Code, [], {}] // 450 + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 10 }, () => false), + editorViewStates: Array.from({ length: 10 }, () => null), + cellTotalHeights: Array.from({ length: 10 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(175); // room for a single header + editor.visibleRanges = [{ start: 3, end: 10 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + // waiting on behavior push to fix this. + test.skip('test4: should render 0, scrolltop halfway through cell 0', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], + ['## header aa', 'markdown', CellKind.Markup, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markup, [], {}], + ['var c = 2;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 8 }, () => false), + editorViewStates: Array.from({ length: 8 }, () => null), + cellTotalHeights: Array.from({ length: 8 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(25); + editor.visibleRanges = [{ start: 0, end: 8 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + // waiting on behavior push to fix this. + test.skip('test5: should render 0->2, scrolltop halfway through cell 2', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], + ['## header aa', 'markdown', CellKind.Markup, [], {}], + ['### header aaa', 'markdown', CellKind.Markup, [], {}], + ['#### header aaaa', 'markdown', CellKind.Markup, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markup, [], {}], + ['var c = 2;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 10 }, () => false), + editorViewStates: Array.from({ length: 10 }, () => null), + cellTotalHeights: Array.from({ length: 10 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(125); + editor.visibleRanges = [{ start: 2, end: 10 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + // waiting on behavior push to fix this. + test.skip('test6: should render 6->7, scrolltop halfway through cell 7', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], + ['## header aa', 'markdown', CellKind.Markup, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['var b = 1;', 'javascript', CellKind.Code, [], {}], + ['# header b', 'markdown', CellKind.Markup, [], {}], + ['## header bb', 'markdown', CellKind.Markup, [], {}], + ['### header bbb', 'markdown', CellKind.Markup, [], {}], + ['var c = 2;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 10 }, () => false), + editorViewStates: Array.from({ length: 10 }, () => null), + cellTotalHeights: Array.from({ length: 10 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(375); + editor.visibleRanges = [{ start: 7, end: 10 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + // waiting on behavior push to fix this. + test.skip('test7: should render 0->1, collapsing against next section', async function () { + await withTestNotebook( + [ + ['# header a', 'markdown', CellKind.Markup, [], {}], //0 + ['## header aa', 'markdown', CellKind.Markup, [], {}], //50 + ['### header aaa', 'markdown', CellKind.Markup, [], {}], //100 + ['#### header aaaa', 'markdown', CellKind.Markup, [], {}], //150 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], //200 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], //250 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], //300 + ['var b = 1;', 'javascript', CellKind.Code, [], {}], //350 + ['# header b', 'markdown', CellKind.Markup, [], {}], //400 + ['## header bb', 'markdown', CellKind.Markup, [], {}], //450 + ['### header bbb', 'markdown', CellKind.Markup, [], {}], + ['var c = 2;', 'javascript', CellKind.Code, [], {}] + ], + async (editor, viewModel) => { + viewModel.restoreEditorViewState({ + editingCells: Array.from({ length: 12 }, () => false), + editorViewStates: Array.from({ length: 12 }, () => null), + cellTotalHeights: Array.from({ length: 12 }, () => 50), + cellLineNumberStates: {}, + collapsedInputCells: {}, + collapsedOutputCells: {}, + }); + + const cellList = createNotebookCellList(instantiationService); + cellList.attachViewModel(viewModel); + cellList.layout(400, 100); + + editor.setScrollTop(350); + editor.visibleRanges = [{ start: 7, end: 12 }]; + + const notebookOutlineEntries = getOutline(editor).entries; + const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); + + await assertSnapshot(resultingMap); + }); + }); + + +}); diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index 54cdf355fcf..c7bcfcf6d3b 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -42,7 +42,7 @@ import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/work import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { CellFindMatchWithIndex, IActiveNotebookEditorDelegate, IBaseCellEditorOptions, ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { NotebookCellStateChangedEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; +import { NotebookCellStateChangedEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { NotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/browser/services/notebookCellStatusBarServiceImpl'; import { ListViewInfoAccessor, NotebookCellList } from 'vs/workbench/contrib/notebook/browser/view/notebookCellList'; import { BaseCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; @@ -61,6 +61,8 @@ import { IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/ import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { FontInfo } from 'vs/editor/common/config/fontInfo'; +import { EditorFontLigatures, EditorFontVariations } from 'vs/editor/common/config/editorOptions'; export class TestCell extends NotebookCellTextModel { constructor( @@ -218,6 +220,8 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic cellList.attachViewModel(viewModel); const listViewInfoAccessor = new ListViewInfoAccessor(cellList); + let visibleRanges: ICellRange[] = [{ start: 0, end: 100 }]; + const notebookEditor: IActiveNotebookEditorDelegate = new class extends mock() { override dispose() { viewModel.dispose(); @@ -289,8 +293,48 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic } override deltaCellDecorations() { return []; } override onDidChangeVisibleRanges = Event.None; - override visibleRanges: ICellRange[] = [{ start: 0, end: 100 }]; + + override get visibleRanges() { + return visibleRanges; + } + + override set visibleRanges(_ranges: ICellRange[]) { + visibleRanges = _ranges; + } + override getId(): string { return ''; } + override setScrollTop(scrollTop: number): void { + cellList.scrollTop = scrollTop; + } + override get scrollTop(): number { + return cellList.scrollTop; + } + override getLayoutInfo(): NotebookLayoutInfo { + return { + width: 0, + height: 0, + scrollHeight: cellList.getScrollHeight(), + fontInfo: new FontInfo({ + pixelRatio: 1, + fontFamily: 'mockFont', + fontWeight: 'normal', + fontSize: 14, + fontFeatureSettings: EditorFontLigatures.OFF, + fontVariationSettings: EditorFontVariations.OFF, + lineHeight: 19, + letterSpacing: 1.5, + isMonospace: true, + typicalHalfwidthCharacterWidth: 10, + typicalFullwidthCharacterWidth: 20, + canUseHalfwidthRightwardsArrow: true, + spaceWidth: 10, + middotWidth: 10, + wsmiddotWidth: 10, + maxDigitWidth: 10, + }, true), + stickyHeight: 0 + }; + } }; return { editor: notebookEditor, viewModel }; @@ -345,7 +389,11 @@ export async function withTestNotebookDiffModel(originalCells: [source: return res; } -export async function withTestNotebook(cells: [source: string, lang: string, kind: CellKind, output?: IOutputDto[], metadata?: NotebookCellMetadata][], callback: (editor: IActiveNotebookEditorDelegate, viewModel: NotebookViewModel, accessor: TestInstantiationService) => Promise | R, accessor?: TestInstantiationService): Promise { +interface IActiveTestNotebookEditorDelegate extends IActiveNotebookEditorDelegate { + visibleRanges: ICellRange[]; +} + +export async function withTestNotebook(cells: [source: string, lang: string, kind: CellKind, output?: IOutputDto[], metadata?: NotebookCellMetadata][], callback: (editor: IActiveTestNotebookEditorDelegate, viewModel: NotebookViewModel, accessor: TestInstantiationService) => Promise | R, accessor?: TestInstantiationService): Promise { const disposables = new DisposableStore(); const instantiationService = accessor ?? setupInstantiationService(disposables); const notebookEditor = _createTestNotebookEditor(instantiationService, cells); From 3d1cf4c6ae0692ba5b8ab515640d521b3cae2df0 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 28 Aug 2023 15:00:41 -0700 Subject: [PATCH 294/607] Fix trustOption string content (#191525) --- .../contrib/workspace/browser/workspace.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index a488d01724c..077b248bc0f 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -319,7 +319,7 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon titleString = this.productService.aiGeneratedWorkspaceTrust.title; checkboxString = this.productService.aiGeneratedWorkspaceTrust.checkboxText; learnMoreString = this.productService.aiGeneratedWorkspaceTrust.startupTrustRequestLearnMore; - trustOption = this.productService.aiGeneratedWorkspaceTrust.startupTrustRequestLearnMore; + trustOption = this.productService.aiGeneratedWorkspaceTrust.trustOption; dontTrustOption = this.productService.aiGeneratedWorkspaceTrust.dontTrustOption; } else { console.warn('AI generated workspace trust dialog contents not available.'); From e50a22d888b8552f9f1237520ab4e0212d5b72ea Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 28 Aug 2023 15:09:44 -0700 Subject: [PATCH 295/607] React to window resize and remove hardcoded maxHeight in Quick Chat (#191531) more Quick Chat polish --- .../contrib/chat/browser/chatQuick.ts | 17 +++++++++---- .../contrib/chat/browser/chatWidget.ts | 24 +++++++++++++++++-- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 76bea4666b7..e30fdf3ca0d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -11,6 +11,7 @@ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IQuickInputService, IQuickWidget } from 'vs/platform/quickinput/common/quickInput'; import { inputBackground, quickInputBackground, quickInputForeground } from 'vs/platform/theme/common/colorRegistry'; import { IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; @@ -121,7 +122,7 @@ export class QuickChatService extends Disposable implements IQuickChatService { class QuickChat extends Disposable { // TODO@TylerLeonhardt: be responsive to window size static DEFAULT_MIN_HEIGHT = 200; - static DEFAULT_MAX_HEIGHT = 900; + private static readonly DEFAULT_HEIGHT_OFFSET = 100; private widget!: ChatWidget; private sash!: Sash; @@ -133,7 +134,8 @@ class QuickChat extends Disposable { @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IChatService private readonly chatService: IChatService, - @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, + @ILayoutService private readonly layoutService: ILayoutService ) { super(); } @@ -182,13 +184,20 @@ class QuickChat extends Disposable { })); this.widget.render(parent); this.widget.setVisible(true); - this.widget.setDynamicChatTreeItemLayout(2, QuickChat.DEFAULT_MAX_HEIGHT); + this.widget.setDynamicChatTreeItemLayout(2, this.maxHeight); this.updateModel(); this.sash = this._register(new Sash(parent, { getHorizontalSashTop: () => parent.offsetHeight }, { orientation: Orientation.HORIZONTAL })); this.registerListeners(parent); } + private get maxHeight(): number { + return this.layoutService.dimension.height - QuickChat.DEFAULT_HEIGHT_OFFSET; + } + private registerListeners(parent: HTMLElement): void { + this._register(this.layoutService.onDidLayout(() => { + this.widget.updateDynamicChatTreeItemLayout(2, this.maxHeight); + })); this._register(this.widget.inputEditor.onDidChangeModelContent((e) => { this._currentQuery = this.widget.inputEditor.getValue(); })); @@ -196,7 +205,7 @@ class QuickChat extends Disposable { this._register(this.widget.onDidChangeHeight((e) => this.sash.layout())); const width = parent.offsetWidth; this._register(this.sash.onDidChange((e) => { - if (e.currentY < QuickChat.DEFAULT_MIN_HEIGHT || e.currentY > QuickChat.DEFAULT_MAX_HEIGHT) { + if (e.currentY < QuickChat.DEFAULT_MIN_HEIGHT || e.currentY > this.maxHeight) { return; } this.widget.layout(e.currentY, width); diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 500ff2b372b..6e1d8b3df5e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -546,14 +546,34 @@ export class ChatWidget extends Disposable implements IChatWidget { return; } - const newHeight = Math.min(renderHeight + diff, maxHeight); + const possibleMaxHeight = (this._dynamicMessageLayoutData?.maxHeight ?? maxHeight); const width = this.bodyDimension?.width ?? this.container.offsetWidth; - const inputPartHeight = this.inputPart.layout(newHeight, width); + const inputPartHeight = this.inputPart.layout(possibleMaxHeight, width); + const newHeight = Math.min(renderHeight + diff, possibleMaxHeight - inputPartHeight); this.layout(newHeight + inputPartHeight, width); }); })); } + updateDynamicChatTreeItemLayout(numOfChatTreeItems: number, maxHeight: number) { + this._dynamicMessageLayoutData = { numOfMessages: numOfChatTreeItems, maxHeight }; + let hasChanged = false; + let height = this.bodyDimension!.height; + let width = this.bodyDimension!.width; + if (maxHeight < this.bodyDimension!.height) { + height = maxHeight; + hasChanged = true; + } + const containerWidth = this.container.offsetWidth; + if (this.bodyDimension?.width !== containerWidth) { + width = containerWidth; + hasChanged = true; + } + if (hasChanged) { + this.layout(height, width); + } + } + layoutDynamicChatTreeItemMode(): void { if (!this.viewModel) { return; From de17d483b24a3a86581e0b2a5abd8c4454495730 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 28 Aug 2023 15:50:21 -0700 Subject: [PATCH 296/607] Have sash take over layouting of Quick Chat until reset (double click) (#191533) The new behavior is that when you use the sash to change the height, then you are locked into that. It's not until you double click, or clear the chat that it returns to the original behavior of being open for just the last question. --- .../contrib/chat/browser/chatQuick.ts | 7 ++++++ .../contrib/chat/browser/chatWidget.ts | 23 +++++++++++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index e30fdf3ca0d..03420bc79b4 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -204,6 +204,9 @@ class QuickChat extends Disposable { this._register(this.widget.onDidClear(() => this.clear())); this._register(this.widget.onDidChangeHeight((e) => this.sash.layout())); const width = parent.offsetWidth; + this._register(this.sash.onDidStart(() => { + this.widget.isDynamicChatTreeItemLayoutEnabled = false; + })); this._register(this.sash.onDidChange((e) => { if (e.currentY < QuickChat.DEFAULT_MIN_HEIGHT || e.currentY > this.maxHeight) { return; @@ -211,6 +214,10 @@ class QuickChat extends Disposable { this.widget.layout(e.currentY, width); this.sash.layout(); })); + this._register(this.sash.onDidReset(() => { + this.widget.isDynamicChatTreeItemLayoutEnabled = true; + this.widget.layoutDynamicChatTreeItemMode(); + })); } async acceptInput(): Promise { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 6e1d8b3df5e..7e65ed72d20 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -207,6 +207,9 @@ export class ChatWidget extends Disposable implements IChatWidget { } clear(): void { + if (this._dynamicMessageLayoutData) { + this._dynamicMessageLayoutData.enabled = true; + } this._onDidClear.fire(); } @@ -525,13 +528,14 @@ export class ChatWidget extends Disposable implements IChatWidget { this._onDidChangeHeight.fire(height); } - private _dynamicMessageLayoutData?: { numOfMessages: number; maxHeight: number }; + private _dynamicMessageLayoutData?: { numOfMessages: number; maxHeight: number; enabled: boolean }; // An alternative to layout, this allows you to specify the number of ChatTreeItems // you want to show, and the max height of the container. It will then layout the // tree to show that many items. + // TODO@TylerLeonhardt: This could use some refactoring to make it clear which layout strategy is being used setDynamicChatTreeItemLayout(numOfChatTreeItems: number, maxHeight: number) { - this._dynamicMessageLayoutData = { numOfMessages: numOfChatTreeItems, maxHeight }; + this._dynamicMessageLayoutData = { numOfMessages: numOfChatTreeItems, maxHeight, enabled: true }; this._register(this.renderer.onDidChangeItemHeight(() => this.layoutDynamicChatTreeItemMode())); const mutableDisposable = this._register(new MutableDisposable()); @@ -556,7 +560,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } updateDynamicChatTreeItemLayout(numOfChatTreeItems: number, maxHeight: number) { - this._dynamicMessageLayoutData = { numOfMessages: numOfChatTreeItems, maxHeight }; + this._dynamicMessageLayoutData = { numOfMessages: numOfChatTreeItems, maxHeight, enabled: true }; let hasChanged = false; let height = this.bodyDimension!.height; let width = this.bodyDimension!.width; @@ -574,8 +578,19 @@ export class ChatWidget extends Disposable implements IChatWidget { } } + get isDynamicChatTreeItemLayoutEnabled(): boolean { + return this._dynamicMessageLayoutData?.enabled ?? false; + } + + set isDynamicChatTreeItemLayoutEnabled(value: boolean) { + if (!this._dynamicMessageLayoutData) { + return; + } + this._dynamicMessageLayoutData.enabled = value; + } + layoutDynamicChatTreeItemMode(): void { - if (!this.viewModel) { + if (!this.viewModel || !this._dynamicMessageLayoutData?.enabled) { return; } const inputHeight = this.inputPart.layout(this._dynamicMessageLayoutData!.maxHeight, this.container.offsetWidth); From 4a6352a816da125e250b736bcb4924ce6428e32b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 28 Aug 2023 15:54:00 -0700 Subject: [PATCH 297/607] Remove toolbar button to @-mention a response (#191534) --- .../chat/browser/actions/chatTitleActions.ts | 38 ------------------- 1 file changed, 38 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index 1b26cbba32b..19bd054e7b8 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -104,44 +104,6 @@ export function registerChatTitleActions() { } }); - registerAction2(class MentionAction extends Action2 { - constructor() { - super({ - id: 'workbench.action.chat.mention', - title: { - value: localize('interactive.mention.label', "Mention"), - original: 'Mention' - }, - f1: false, - category: CHAT_CATEGORY, - icon: Codicon.add, - menu: { - id: MenuId.ChatMessageTitle, - group: 'navigation', - order: 3, - when: CONTEXT_RESPONSE - } - }); - } - - run(accessor: ServicesAccessor, ...args: any[]) { - const item = args[0]; - if (!isResponseVM(item)) { - return; - } - - const chatWidgetService = accessor.get(IChatWidgetService); - const widget = chatWidgetService.lastFocusedWidget!; - const num = widget.viewModel!.getItems() - .filter(isResponseVM) - .indexOf(item) + 1; - widget.inputEditor.setValue(`${widget.inputEditor.getValue()} @response:${num} `); - const lastLine = widget.inputEditor.getModel()!.getLineCount(); - const lastCol = widget.inputEditor.getModel()!.getLineLength(lastLine); - widget.inputEditor.setSelection({ startColumn: lastCol, endColumn: lastCol, startLineNumber: lastLine, endLineNumber: lastLine }); - } - }); - registerAction2(class InsertToNotebookAction extends Action2 { constructor() { super({ From 3af6e2b9d18c5d92d212f377d57195996fbdc3e2 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 28 Aug 2023 16:25:14 -0700 Subject: [PATCH 298/607] behavior fixes, align more closely with editor --- .../viewParts/notebookEditorStickyScroll.ts | 92 +++++++++++++------ 1 file changed, 63 insertions(+), 29 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index 7b490223afd..414acaa14be 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -79,6 +79,16 @@ export class NotebookStickyLine extends Disposable { } } +// TODO @Yoyokrazy: +// BEHAVIOR +// - [ ] bug with some popping around the cell transition +// - [ ] bug with only bottom most sticky being partially transitioned +// - partial rendering/transition only occuring when the headers shrink against a new section +// - **and only for BOTTOM of that initial sticky tree** +// - issues with HC themes +// UX +// - [ ] render symbols instead of #'s? +// - maybe 'Hx >' where x is the level export class NotebookStickyScroll extends Disposable { private readonly _disposables = new DisposableStore(); private currentStickyLines = new Map(); @@ -178,7 +188,10 @@ export class NotebookStickyScroll extends Disposable { while (left <= right) { const mid = Math.floor((left + right) / 2); - if (notebookOutlineEntries[mid].index < visibleIndex) { + if (notebookOutlineEntries[mid].index === visibleIndex) { + bucket = mid; + break; + } else if (notebookOutlineEntries[mid].index < visibleIndex) { bucket = mid; left = mid + 1; } else { @@ -218,15 +231,13 @@ export class NotebookStickyScroll extends Disposable { if (!cell) { return; } - if (cell.cellKind === CellKind.Markup) { - continue; - } // if we are here, the cell is a code cell. - // check next visible cell, if markdown, that means this is the end of the section - const nextVisibleCell = this.notebookEditor.cellAt(i + 1); - if (nextVisibleCell && i + 1 < visibleRange.end) { - if (nextVisibleCell.cellKind === CellKind.Markup) { + // check next cell, if markdown, that means this is the end of the section + // check if cell is within visible range + const nextCell = this.notebookEditor.cellAt(i + 1); + if (nextCell && i + 1 < visibleRange.end) { + if (nextCell.cellKind === CellKind.Markup) { // this is the end of the section // store the bottom scroll position of this cell sectionBottom = this.notebookCellList.getCellViewScrollBottom(cell); @@ -281,6 +292,9 @@ export class NotebookStickyScroll extends Disposable { static computeStickyHeight(entry: OutlineEntry) { let height = 0; + if (entry.cell.cellKind === CellKind.Markup) { + height += 22; + } while (entry.parent) { height += 22; entry = entry.parent; @@ -289,7 +303,6 @@ export class NotebookStickyScroll extends Disposable { } static renderStickyLines(entry: OutlineEntry | undefined, containerElement: HTMLElement, numLinesToRender: number, newMap: Map, notebookEditor: INotebookEditor) { - const partial = false; let currentEntry = entry; const elementsToRender = []; @@ -299,12 +312,24 @@ export class NotebookStickyScroll extends Disposable { currentEntry = currentEntry.parent; continue; } - const lineToRender = NotebookStickyScroll.createStickyElement(currentEntry, partial, notebookEditor); + const lineToRender = NotebookStickyScroll.createStickyElement(currentEntry, notebookEditor); newMap.set(currentEntry, { line: lineToRender, rendered: false }); elementsToRender.unshift(lineToRender); currentEntry = currentEntry.parent; } + // TODO: clean up partial cell animation + // [ ] slight pop as lines finish disappearing + // [ ] only actually works when shrunk against new section. **and only for BOTTOM of that initial sticky tree** + // [ ] issues with HC themes + // use negative margins to render the bottom sticky line as a partial element + // todo: partial render logic here + // if (numLinesToRender % 1 !== 0) { + // const partialHeight = 22 - Math.floor((numLinesToRender % 1) * 22); + // elementsToRender[elementsToRender.length - 1].element.style.zIndex = '-1'; + // elementsToRender[elementsToRender.length - 1].element.style.marginTop = `-${partialHeight}px`; + // } + // iterate over elements to render, and append to container // break when we reach numLinesToRender for (let i = 0; i < elementsToRender.length; i++) { @@ -319,17 +344,10 @@ export class NotebookStickyScroll extends Disposable { return newMap; } - static createStickyElement(entry: OutlineEntry, partial: boolean, notebookEditor: INotebookEditor) { + static createStickyElement(entry: OutlineEntry, notebookEditor: INotebookEditor) { const stickyElement = document.createElement('div'); stickyElement.classList.add('notebook-sticky-scroll-line'); stickyElement.innerText = '#'.repeat(entry.level) + ' ' + entry.label; - - // todo: partial line rendering for animation - // if (partial) { - // const partialHeight = Math.floor(remainder * 22); - // stickyLine.style.height = `${partialHeight}px`; - // } - return new NotebookStickyLine(stickyElement, entry, notebookEditor); } @@ -364,15 +382,26 @@ export function computeContent(domNode: HTMLElement, notebookEditor: INotebookEd let trackedEntry = undefined; let sectionBottom = 0; for (let i = visibleRange.start; i < visibleRange.end; i++) { - if (i === 0) { // don't show headers when you're viewing the top cell - return new Map(); - } const cell = notebookEditor.cellAt(i); if (!cell) { return new Map(); } + + // account for transitions between top level headers if (cell.cellKind === CellKind.Markup) { - continue; + sectionBottom = notebookCellList.getCellViewScrollBottom(cell); + const entry = NotebookStickyScroll.getVisibleOutlineEntry(i, notebookOutlineEntries); + if (!entry) { + return new Map(); + } + + if (!entry.parent) { + // if the cell is a top level header, only render once we have scrolled past the bottom of the cell + // todo: (polish) figure out what padding value to use here. need to account properly for bottom insert cell toolbar, cell toolbar, and md cell bottom padding + if (sectionBottom > editorScrollTop) { + return new Map(); + } + } } // if we are here, the cell is a code cell. @@ -393,37 +422,42 @@ export function computeContent(domNode: HTMLElement, notebookEditor: INotebookEd if (editorScrollTop + currentSectionStickyHeight < sectionBottom) { const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); let newMap: Map = new Map(); - newMap = NotebookStickyScroll.renderStickyLines(entry?.parent, domNode, linesToRender, newMap, notebookEditor); + newMap = NotebookStickyScroll.renderStickyLines(entry, domNode, linesToRender, newMap, notebookEditor); return newMap; } let nextSectionEntry = undefined; for (let j = 1; j < visibleRange.end - i; j++) { - // find next code cell after this one + // find next section after this one const cellCheck = notebookEditor.cellAt(i + j); - if (cellCheck && cellCheck.cellKind === CellKind.Code) { + if (cellCheck) { nextSectionEntry = NotebookStickyScroll.getVisibleOutlineEntry(i + j, notebookOutlineEntries); - break; + if (nextSectionEntry) { + break; + } } } const nextSectionStickyHeight = NotebookStickyScroll.computeStickyHeight(nextSectionEntry!); + // recompute section bottom based on the top of the next section + sectionBottom = notebookCellList.getCellViewScrollTop(nextSectionEntry!.cell) - 10; + // this block of logic cleans transitions between two sections that share a parent. // if the current section and the next section share a parent, then we can render the next section's sticky lines to avoid pop-in between if (entry?.parent?.parent === nextSectionEntry?.parent) { - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22) + 1; + const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22) + 100; let newMap: Map = new Map(); newMap = NotebookStickyScroll.renderStickyLines(nextSectionEntry?.parent, domNode, linesToRender, newMap, notebookEditor); return newMap; } else if (Math.abs(currentSectionStickyHeight - nextSectionStickyHeight) > 22) { // only shrink sticky - const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); + const linesToRender = (sectionBottom - editorScrollTop) / 22; let newMap: Map = new Map(); newMap = NotebookStickyScroll.renderStickyLines(entry?.parent, domNode, linesToRender, newMap, notebookEditor); return newMap; } } } else { - // there is no next cell, so use the bottom of the editor as the sectionBottom, using scrolltop + height + // there is no next visible cell, so use the bottom of the editor as the sectionBottom, using scrolltop + height sectionBottom = notebookEditor.getLayoutInfo().scrollHeight; trackedEntry = NotebookStickyScroll.getVisibleOutlineEntry(i, notebookOutlineEntries); const linesToRender = Math.floor((sectionBottom - editorScrollTop) / 22); From c0c489f19111c64c05e711d809ab4c3ce32687a9 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 28 Aug 2023 17:00:51 -0700 Subject: [PATCH 299/607] behavior fixes for nb sticky scroll + css fix --- .../browser/media/notebookEditorStickyScroll.css | 10 ++++++++++ .../browser/viewParts/notebookEditorStickyScroll.ts | 7 ++++--- ...llapsing_against_equivalent_level_header_0.snap} | 0 .../test/browser/notebookStickyScroll.test.ts | 13 ++++++------- 4 files changed, 20 insertions(+), 10 deletions(-) rename src/vs/workbench/contrib/notebook/test/browser/__snapshots__/{NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_third_header_0.snap => NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_equivalent_level_header_0.snap} (100%) diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css index 8107bfb1282..5750b20cd65 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css @@ -15,7 +15,17 @@ .notebookOverlay .notebook-sticky-scroll-container .notebook-sticky-scroll-line { + background-color: var(--vscode-notebook-editorBackground); + position: relative; + z-index: 0; padding-left: 12px; + /* transition: margin-top 0.2s ease-in-out; */ +} + +.monaco-workbench.hc-light .notebookOverlay .notebook-sticky-scroll-container, +.monaco-workbench.hc-black .notebookOverlay .notebook-sticky-scroll-container { + background-color: var(--vscode-editorStickyScroll-background); + border-bottom: 1px solid var(--vscode-contrastBorder); } .monaco-workbench diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index 414acaa14be..426f767e89d 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -387,6 +387,8 @@ export function computeContent(domNode: HTMLElement, notebookEditor: INotebookEd return new Map(); } + const nextCell = notebookEditor.cellAt(i + 1); + // account for transitions between top level headers if (cell.cellKind === CellKind.Markup) { sectionBottom = notebookCellList.getCellViewScrollBottom(cell); @@ -406,9 +408,8 @@ export function computeContent(domNode: HTMLElement, notebookEditor: INotebookEd // if we are here, the cell is a code cell. // check next cell, if markdown, that means this is the end of the section - const nextVisibleCell = notebookEditor.cellAt(i + 1); - if (nextVisibleCell && i + 1 < visibleRange.end) { - if (nextVisibleCell.cellKind === CellKind.Markup) { + if (nextCell && i + 1 < visibleRange.end) { + if (nextCell.cellKind === CellKind.Markup) { // this is the end of the section // store the bottom scroll position of this cell sectionBottom = notebookCellList.getCellViewScrollBottom(cell); diff --git a/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_third_header_0.snap b/src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_equivalent_level_header_0.snap similarity index 100% rename from src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_third_header_0.snap rename to src/vs/workbench/contrib/notebook/test/browser/__snapshots__/NotebookEditorStickyScroll_test3__should_render_0-_1___collapsing_against_equivalent_level_header_0.snap diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts index 46bd58ded14..708b8b7aea8 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts @@ -170,8 +170,7 @@ suite('NotebookEditorStickyScroll', () => { }); }); - // waiting on behavior push to fix this. - test.skip('test3: should render 0->1, collapsing against equivalent level header', async function () { + test('test3: should render 0->1, collapsing against equivalent level header', async function () { await withTestNotebook( [ ['# header a', 'markdown', CellKind.Markup, [], {}], // 0 @@ -209,7 +208,7 @@ suite('NotebookEditorStickyScroll', () => { }); }); - // waiting on behavior push to fix this. + // outdated/improper behavior test.skip('test4: should render 0, scrolltop halfway through cell 0', async function () { await withTestNotebook( [ @@ -236,7 +235,7 @@ suite('NotebookEditorStickyScroll', () => { cellList.attachViewModel(viewModel); cellList.layout(400, 100); - editor.setScrollTop(25); + editor.setScrollTop(50); editor.visibleRanges = [{ start: 0, end: 8 }]; const notebookOutlineEntries = getOutline(editor).entries; @@ -246,7 +245,7 @@ suite('NotebookEditorStickyScroll', () => { }); }); - // waiting on behavior push to fix this. + // outdated/improper behavior test.skip('test5: should render 0->2, scrolltop halfway through cell 2', async function () { await withTestNotebook( [ @@ -285,7 +284,7 @@ suite('NotebookEditorStickyScroll', () => { }); }); - // waiting on behavior push to fix this. + // outdated/improper behavior test.skip('test6: should render 6->7, scrolltop halfway through cell 7', async function () { await withTestNotebook( [ @@ -325,7 +324,7 @@ suite('NotebookEditorStickyScroll', () => { }); // waiting on behavior push to fix this. - test.skip('test7: should render 0->1, collapsing against next section', async function () { + test('test7: should render 0->1, collapsing against next section', async function () { await withTestNotebook( [ ['# header a', 'markdown', CellKind.Markup, [], {}], //0 From 8ef696178913af8c62ae7ee1f613904395dcf34e Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 28 Aug 2023 17:48:09 -0700 Subject: [PATCH 300/607] server-web: implement secret storage provider (#191538) Works quite similarly to vscode.dev. The client has a key stored in secret storage. The server has a key stored server-side, and issues an http-only cookie to the client. The client can ask the server to combine its key and the http-only cookie key to a key component, which it combines with its local key to encrypt and decrypt data. This logic kicks in if the web server bits see a `vscode-secret-key-path` cookie set when it loads. --- cli/src/commands/serve_web.rs | 177 ++++++++++++-- cli/src/tunnels/socket_signal.rs | 2 +- src/vs/code/browser/workbench/workbench.ts | 266 ++++++++++++++++----- 3 files changed, 366 insertions(+), 79 deletions(-) diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs index 4a3af432444..8d37427dd33 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -10,6 +10,7 @@ use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; +use const_format::concatcp; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Request, Response, Server}; use tokio::io::{AsyncBufReadExt, BufReader}; @@ -23,6 +24,7 @@ use crate::constants::VSCODE_CLI_QUALITY; use crate::download_cache::DownloadCache; use crate::log; use crate::options::Quality; +use crate::state::{LauncherPaths, PersistedState}; use crate::update_service::{ unzip_downloaded_release, Platform, Release, TargetKind, UpdateService, }; @@ -48,6 +50,22 @@ const SERVER_ACTIVE_TIMEOUT_SECS: u64 = SERVER_IDLE_TIMEOUT_SECS * 24 * 30 * 12; /// How long to cache the "latest" version we get from the update service. const RELEASE_CACHE_SECS: u64 = 60 * 60; +/// Number of bytes for the secret keys. See workbench.ts for their usage. +const SECRET_KEY_BYTES: usize = 32; +/// Path to mint the key combining server and client parts. +const SECRET_KEY_MINT_PATH: &str = "/_vscode-cli/mint-key"; +/// Cookie set to the `SECRET_KEY_MINT_PATH` +const PATH_COOKIE_NAME: &str = "vscode-secret-key-path"; +/// Cookie set to the `SECRET_KEY_MINT_PATH` +const PATH_COOKIE_VALUE: &str = concatcp!( + PATH_COOKIE_NAME, + "=", + SECRET_KEY_MINT_PATH, + "; SameSite=Strict; Path=/" +); +/// HTTP-only cookie where the client's secret half is stored. +const SECRET_KEY_COOKIE_NAME: &str = "vscode-cli-secret-half"; + /// Implements the vscode "server of servers". Clients who go to the URI get /// served the latest version of the VS Code server whenever they load the /// page. The VS Code server prefixes all assets and connections it loads with @@ -69,10 +87,14 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result(service) } }; @@ -106,35 +128,82 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result, log: log::Logger, - req: Request, -) -> Result, Infallible> { - let release = if let Some((r, _)) = get_release_from_path(req.uri().path(), cm.platform) { + server_secret_key: SecretKeyPart, +} + +/// Handler function for an inbound request +async fn handle(ctx: HandleContext, req: Request) -> Result, Infallible> { + let client_key_half = get_client_key_half(&req); + let mut res = match req.uri().path() { + SECRET_KEY_MINT_PATH => handle_secret_mint(ctx, req), + _ => handle_proxied(ctx, req).await, + }; + + append_secret_headers(&mut res, &client_key_half); + + Ok(res) +} + +async fn handle_proxied(ctx: HandleContext, req: Request) -> Response { + let release = if let Some((r, _)) = get_release_from_path(req.uri().path(), ctx.cm.platform) { r } else { - match cm.get_latest_release().await { + match ctx.cm.get_latest_release().await { Ok(r) => r, Err(e) => { - error!(log, "error getting latest version: {}", e); - return Ok(response::code_err(e)); + error!(ctx.log, "error getting latest version: {}", e); + return response::code_err(e); } } }; - Ok(match cm.get_connection(release).await { + match ctx.cm.get_connection(release).await { Ok(rw) => { if req.headers().contains_key(hyper::header::UPGRADE) { - forward_ws_req_to_server(cm.log.clone(), rw, req).await + forward_ws_req_to_server(ctx.log.clone(), rw, req).await } else { forward_http_req_to_server(rw, req).await } } Err(CodeError::ServerNotYetDownloaded) => response::wait_for_download(), Err(e) => response::code_err(e), - }) + } +} + +fn handle_secret_mint(ctx: HandleContext, req: Request) -> Response { + use sha2::{Digest, Sha256}; + + let mut hasher = Sha256::new(); + hasher.update(ctx.server_secret_key.0.as_ref()); + hasher.update(get_client_key_half(&req).0.as_ref()); + let hash = hasher.finalize(); + let hash = hash[..SECRET_KEY_BYTES].to_vec(); + response::secret_key(hash) +} + +/// Appends headers to response to maintain the secret storage of the workbench: +/// sets the `PATH_COOKIE_VALUE` so workbench.ts knows about the 'mint' endpoint, +/// and maintains the http-only cookie the client will use for cookies. +fn append_secret_headers(res: &mut Response, client_key_half: &SecretKeyPart) { + let headers = res.headers_mut(); + headers.append( + hyper::header::SET_COOKIE, + PATH_COOKIE_VALUE.parse().unwrap(), + ); + headers.append( + hyper::header::SET_COOKIE, + format!( + "{}={}; SameSite=Strict; HttpOnly; Max-Age=2592000; Path=/", + SECRET_KEY_COOKIE_NAME, + client_key_half.encode() + ) + .parse() + .unwrap(), + ); } /// Gets the release info from the VS Code path prefix, which is in the @@ -258,6 +327,77 @@ fn is_commit_hash(s: &str) -> bool { s.len() == COMMIT_HASH_LEN && s.chars().all(|c| c.is_ascii_hexdigit()) } +/// Gets a cookie from the request by name. +fn extract_cookie(req: &Request, name: &str) -> Option { + for h in req.headers().get_all(hyper::header::COOKIE) { + if let Ok(str) = h.to_str() { + for pair in str.split("; ") { + let i = match pair.find('=') { + Some(i) => i, + None => continue, + }; + + if &pair[..i] == name { + return Some(pair[i + 1..].to_string()); + } + } + } + } + + None +} + +#[derive(Clone)] +struct SecretKeyPart(Box<[u8; SECRET_KEY_BYTES]>); + +impl SecretKeyPart { + pub fn new() -> Self { + let key: [u8; SECRET_KEY_BYTES] = rand::random(); + Self(Box::new(key)) + } + + pub fn decode(s: &str) -> Result { + use base64::{engine::general_purpose, Engine as _}; + let mut key: [u8; SECRET_KEY_BYTES] = [0; SECRET_KEY_BYTES]; + let v = general_purpose::URL_SAFE.decode(s)?; + if v.len() != SECRET_KEY_BYTES { + return Err(base64::DecodeSliceError::OutputSliceTooSmall); + } + + key.copy_from_slice(&v); + Ok(Self(Box::new(key))) + } + + pub fn encode(&self) -> String { + use base64::{engine::general_purpose, Engine as _}; + general_purpose::URL_SAFE.encode(self.0.as_ref()) + } +} + +/// Gets the server's half of the secret key. +fn get_server_key_half(paths: &LauncherPaths) -> SecretKeyPart { + let ps = PersistedState::new(paths.root().join("serve-web-key-half")); + let value: String = ps.load(); + if let Ok(sk) = SecretKeyPart::decode(&value) { + return sk; + } + + let key = SecretKeyPart::new(); + let _ = ps.save(key.encode()); + key +} + +/// Gets the client's half of the secret key. +fn get_client_key_half(req: &Request) -> SecretKeyPart { + if let Some(c) = extract_cookie(req, SECRET_KEY_COOKIE_NAME) { + if let Ok(sk) = SecretKeyPart::decode(&c) { + return sk; + } + } + + SecretKeyPart::new() +} + /// Module holding original responses the CLI's server makes. mod response { use const_format::concatcp; @@ -287,6 +427,14 @@ mod response { .body(Body::from(concatcp!("The latest version of the ", QUALITYLESS_SERVER_NAME, " is downloading, please wait a moment...", ))) .unwrap() } + + pub fn secret_key(hash: Vec) -> Response { + Response::builder() + .status(200) + .header("Content-Type", "application/octet-stream") // todo: get latest + .body(Body::from(hash)) + .unwrap() + } } /// Handle returned when getting a stream to the server, used to refcount @@ -515,6 +663,7 @@ impl ConnectionManager { let executable = path .join("bin") .join(args.release.quality.server_entrypoint()); + let socket_path = get_socket_name(); #[cfg(not(windows))] diff --git a/cli/src/tunnels/socket_signal.rs b/cli/src/tunnels/socket_signal.rs index 69feddade61..53e6cd51567 100644 --- a/cli/src/tunnels/socket_signal.rs +++ b/cli/src/tunnels/socket_signal.rs @@ -288,7 +288,7 @@ mod tests { } } - const TEST_191501_BUFS: [&'static str; 3] = [ + const TEST_191501_BUFS: [&str; 3] = [ "TMzLSsQwFIDhfSDv0NXsYs2kubQQXIgX0IUwHVyfpCdjaSYZmkjRpxdEBnf/5vufHsZmK0PbxuwhfuRS2zmVecKVBd1rEYTUqL3gCoxBY7g2RoWOg+nE7Z4H1N3dij6nhL7OOY15wWTBeN87IVkACayTijMXcGJagevkxJ3i/e4/swFiwV1Z5ss7ukP2C9bHFc5YbF0/sXkex7eW33BK7q9maI6X0woTUvIXQ7OhK7+YkgN6dn2xF/wamhTgVM8xHl8Tr2kvvv2SymYtJZT8AAAA//8=", "YmJAgIhqpZLKglQlK6XE0pIMJR0IZaVUlJqbX5JaXAwSSkksSQQK+WUkung5BWam6TumVaWEFhQHJBuUGrg4WUY4eQV4GOTnhwVkWJiX5lRmOdoq1QIAAAD//w==", "jHdTdCZQk23UsW3btpOObeuLbdu2bdvs2E46tm17+p+71ty5b/ect13aVbte6n8XmfmfIv9rev8BaP8BNjYWzv8s/78S/ItxsjCzNTEW/T+s2DhZaNSE5Bi41B0kFBjZ2VjYtAzlzTWUHJWtJC2dPFUclDmZPW2EFQEAGkN3Rb7/tGPiZOFoYizy/1LhZvnXu6OZEzG3F/F/duNf6v/Zk39B9naO/yAuRi5GHx8FeWUVQob/JZTEPx9uQiZmDnrGf5/pv93+KeX0b7OEzExs/9kALo7WDBz0nEz0/wxCAICJ/T+QmoH6v0V2/udCJ2Nia+Zs/i8L47/3f+H/cOMmNLS3t7YAGP6HLIM7nZubG52pnaMN3b+kJrYAO2MT4//IGvKquY+4Oly7Z01ajWRItkE1jacYu9tcSU339/OnBkYgUbBD9rHonA9pvJV7heYuoFUpRcnKi8RwoJrSkW7ePD6N3ANHPr1UW7wPu5907dLnd4hlXwziROJkDgejfKv5ztZzPgXoUaEPEsM6y752iLyMJdkKwrSo+LAiaFp4HSRvSAnMT2Ck9JHIyQNuaFslDhaLQMIP+B7AGRyZFXeqpFF8HvfFVkQHqGejNjdizFvRHkndAl8AtfEqRHfxPFAit0twsNMyaONmusi/YHvmbQhpTRnyOV0gg+tXzisWmDsLBFAutCcGRHR0Cigere6p3A7NDGmBxHAZSmK/LGHKCeyUqN9fyBIUmyCtV99ptMaQWt4KAny5Fg+nTU1gBvBq4RvHlGCF9WL+2ZxKDfB2gr2GQaUY76Tv7x79VKbxwC5GITg2q02XPy6ZNFnLryVCGskiYPFPQLAsU+LrTvbyQTk7KNUFHwzBUTP1MiKg9LCdWAs8BZx3FHYaJyvIPw4nJpUAP3rP8GPdJeb3iIJ7i8xf15F71iT47rNv+qCXaQD9NBo8PcRVqnEy3vyrPG5SO8HwSDk9PhQJe2xo4Q52soIDB3v1jYYmR8ZkuoNq3Moy6BDjR1WBCTFJEHjdSSADxzRJ2hnozSOLmzTLuKgwWnFU1aGpQ5S8Ry7ME7gVb+CwnFvVtrpofL+DXvE3CY9Fhqe0y4Sq1yLyn/vcgA7ShFG+QnTB5zaKS3Ndj6LSCxwiNivY9R9TsAXobw4Exqog7xCAjYxNIbDuo/fC1QKpFUzvxw+7Rjc8J2lJg80YveK++I5fqJVAFu0Gb4SuJAd8ernBkpyy9lbou0enEfQMOjjucNiy+rgpU4pl+ERgt/Be+8G9l0RbeUwthLZp4ARnBHAB2mcB2o1cJIbhXnMiYStLmjwI+i+NOhBvRV8nmAVslkGdsEVU6Q3hYy/cT/QRTbEF0W58bkYPCyx93ESp7/sWkTG5i9GInCwW+zw1NIRfi2zkuz7KIzOlg33b5/R60L2tjlPtcLjZYL9qGWXwgPApKkndbDq0HhRCQYTyEZ1nC4MFi9NuasFm4t4UV4/W4L0A8YwsXH2m8Rh7hl1No5oIIlAGi5Er/amKw5mAA/Hvwbzfd4TGx66MHWA9t6NAA2WPx538griN7LCqE2315o09fNbOumI6fM1CN0AJT2FheQgaG4tdPFPn6uAeDXUDT8OkTdRFNi6Av4rwo6NnyfLnLYxBNdAhHs75bAedI5egbRrWLC48JT7aKsV+VsOmLsk0TGh6ISxI3WzskVbVFr6HGLy8jee1ZiMF0wzd/B4LvlyGIMa6HD+JBsGOH6vukgqV7ywTl6P+Wo8mTZHo12d7u09Z59eyXJcZKnqY4YzEzGUrlGzvO0Rgfgsse3RMPWJSpsETWqo5zMTtzYk9HANeoA5ubNoO/jjtLyModk/iH6XLiFD1591q+nXNb3Ve2v/aHlJQQYaytpOULvnsEYGIQH9+y3eK1Rgqgs7fxD3uzpv06A/afiToieIJpbjLhy3JZBEAmtN5UgJm6SuCbqgKJ+fDsuwMp/m0fCNVqrYORcBpKTvIWFzWF/leWJntKUis0dPrWy5x7Yu2GhqJh3GN2bT8w1uIh1haSlBmhMOzV3yNUmNcjqFV+GziNt6twoPDJ+4m7TE7hP2E9mEhiYihUDjT0X2Q4k0GIqdIl6fpoFPK0zdfRfbEkP2Ulr7fzfVqCYp9iuxtZFqBafBWLNHVjYtIn9/Z6Z3mP8DBfOYrXbMXldLjKW6rHr3w/LACe+LINkxcxQ9rxxBffepkhhj8NQ7vpyXpudfYmfPMsnai+b5VI5QMcyZly26kxMo6KGGilNYyX/hLaowV4GjIEY7kHRCNmJIBNevb1ag4w98wLWMtfyPMLn18o9cFKiJk2kjZmRBFh0S0Bd7AjxiNO8YdDQ83lBGS5JrxmLG+hW2oGYQllWS2UjK3+loONmC6NpPNgUiNhDQ05s24iRJZ/bzrgBskPLGukoMu8NK8CQNKZE8zzmsCrnkU53iPeZd/UT8ox6WMMZOtDv8YyQpTmhbzXCQW9ogbfgqH447dJFZuPkT4MGfKw+0c5L6aLWqAadBU9yLftFVsi8GZOSB9Ctv9/fJZ5SmlNgt25uGvspB9y1PQGEmLQyjFiGK7kveEw4Knn9lv/9GV2YlCdeRTAUyOS56k6G4ajfxNtMHPaDqIWTM1yBem3dShwkhD0nMXit14/wHRHosy59T+nkuvxG1MbTx8GJM45rvrOmUW0nwxNNdsdqFCNPWn+GcYzIdwCNFtHmdSKNOecfZZVJnKzuGbs41wRQIkv1E1p6ITiPxv+zKWflEU76wHOPrDx4rmyw3Z6MqaP316eOcW43JwBvp9hJuMUHr0TFkvjd5KzvmUSrZfYvpPZ2humVwOsjChiFzc7aoBMt8MdXyf2LIhuhBAg8Ue3wLqlg3cEYBS2z+uzrS5bJzmzH3NGmI+M/WbHOkbqcNtSoZjwp4NI5bSpCKWs7BqrK8sfsUC+UpA08Lfc4CpcBmsTyuHncO2gLc9jPMT+SBAgiZxTDncaiM+YG19ntqYSttys+jpASZDwEWjYRN8QURClAIs0G0KKoY0jjWcc0rypYXiCsHD9+kjtnYJHuzeZw2GQ5U5j7acLM8nyuy8bSJaKZXFq8TJkQ/p4lSkKHpVQPi+dWF4jYaQFEGiPAuiLOGzOE/f8B2rePs9zps7QivUyIiM8fsbPx5mwaC7FbjdihjbM198akLx99SpXAF4fh6d/xwLppw2kFrKa0UsTa/emTuV+6l2/8WmVWLd8JJAhcE+qbMrJBrohgGdDNZIRxJOrsFCzSmu2ykTCZnZlPITlbK/hUA/+DwdtJbmzKczEWAS9ENNbxHNSbn4Nqsz0yvhUE2a/FT6tvnBbXm/X2yLQQhxuVyNCsK2TeUNifqlsCEAJAALqqNI/NX+owJEAk+KehT/fpCsXGTsT3kFsUiPNWAkOEuHviK3Nzpu53edKRZgInWOWhGnd8aD6k7kio0tLT8i/PkxVrdZftlNrqPZfiEXkqX3hM526HzLGVzlr+CvTBKxsU8ROxHvBGWzJk4Tt0uDhZessy5BDFVx2xiYxMTXfQyv8NF0Op3CKCFvH1KbE2Z2TGCvpOEH7LKVK5TyTVSP+yah8TkpL1cHorIRxz2a5cMNMZGgdooqszII7PJuT3Ii0GpCCXe3v5mzysGhVKBulynWOeMrlJ4jKA4xzAXIg7ReLCGOntAOvU7qD+5UBufLWxx/3cqhuMcZDnR2dUjJuFG5LuFiwnvboFRMjVTvVJkcNdUc7b+0auIQWC1E3hTQx422OCMuGvayP3WMCGe8IClwSw4f1uA5LkoDYZbVQo1SUzETYNPQUK5BTJy7YRq4ln9vLvDHDImNd3TiWnsL7Zp9qWVSSTfSVSyZTT4fJqKIZ/Kcy7IkXFyv0Frw64R7y0vM+tAu+0kebn9y+DlN2xmi7nmf81iI1xffS5+ehMzQJTIa8SjVc8kCf14eOLiR7TgCnHcJieDFQI9r9K9co2G0hpitdihrbb56XvossnHl8Fu4JRLBPgKXsAQyX3v3BUHuw42rmeQXz74oZzmEIG13oteilg9HOUyoR5NHE94cYtIqP80qheAh9uQA9e3+TSmiLy6dsU625mYOYcPixVm9ZYuiOtLWQ3tT8j2T111qqjqNu6yUSxlIAh0+ANUEhEh9Uoj9v89/WqlGXNWPDmKfRtn+yFVoyggl8PjW0GB7qfreaEuoqouCGoV+lWma6sNZyKYQGIn51nzIyO1uUlRQZq5j8aTQgcXlNYi5rXALJ2Kj8nEbJT8OqXEt0fbWPKaLQZch23yR9RLyaXMpTIzzRBkoFY5g0MfTWFLbcMynydkZITcfLTSDeD/fxSqUzWmgjk9j1aQ07KUBInTRErSbfEhgCVikEENWXpOubo3XV4YBv9CJYSuXnSv0d3jLQdHefqwT7+Gyqy0ZJYicFYw3ma+acapIZw2r4qg4BNKbSbkMKOuWidsr1dxjS9bjSYoNH/VDBdbgXpXTpPJosDIjwMHsV48OfhwZjvnAC0r2yJ3+NPhBP4g/GU14mpdefzvR08OElSHLpZidGsL5GGtpzcohM5sQ48TMsOs6Cy3vvgKR1oanGjGa8dRN+UaaAWm1dieSOjvXzIIVPp3zoKEgVu9zlP2W5NtNSVDfceVy/cA2IFjOlKa5EiLEEA57fuxvGmOvxCB+ZROvg6KOi6EbxLMylQEbvzctlbmEJ0S32x1usYisIWFfCLX/SEETVFuAxZJej9AcvkolOkSLNlohZdKzOYeRMfQM/RMT4JwSfFqHgIq4XeYPtTzMO2ZkTdOjdrrWL0ZMFosuXiKD/9qKKbo1FjqjwiT5a4uIaPdU95J52kiPoS7adOxUFiypbB9SrLFTABESJrPr0qMSVCi9cMME+Vt2Qq9gYFIvXoDRAR0SP04c/2A1r/tvxBu6JRGDB9cwYWOE1g8W+W/vju6WwPvifEO4AQ+KD3bGEhffrUWM1SnsAZBbJOgep/M1iU/HX4uNGb6Dmz+0PQdJAo7TkA2D+Wigyb9CQUfK16vwLvIIvMnylTcOOIAUtbiy2/lcdbmnQcFMt7ZZLQxBemf8S5L8jkyl1WLZyVNGDm5qf/72TQLs6KK4ljCJqMt0F6p8tidu/52WK95lYzKiZy6nlOSKadsCEWX5+eMzpJu8ZjYF5Qf1K54q5wO/T4Y+QYoWlUlXB6MoL0adwXmSs5T7Mht+6k8BO7T5I+3iI54WdYwixTnvlI/TNQSjwGJdxqJOmInihyKgkCx1lUyn/fx6jKZ+1MHPZwvfOg5V9TuCf+aXvjVhcgJHJBilS8ytrZh8FQh23yNbEIMoE6lYyWuYdSKv6831VdffGAP6gvaD3d9aUBJRkHquA1iqVB/ZG+bcJLpeMFJagd95AvGXUIuYwFKFmBtlKkjOuiEbKNKxv+SJ/NQCIGRBxVkm6oqcabuFnskNEhB4FnYnplnCIUZEfsuLirqsm6sSQZ2ZITdUAkmQ308cj5051V8FwogjNmZJyYuNNsOxYzumG33B7Z5k6QHkr2HC4aky5ZHP2bW8quZNaSXEcL5YGfZeTPTOVCv3TA+e4NLZeVocXTUYNWe7pyYjaf6EUeHdXOAMpZk9084KP8PBCwnlNfiZG2fXD+36bvn8sOVcsLvwAT01LEmVgo2E0geZqDPd8OIHJxDVB7VXNeFYIKjKgOjT63Bq49GLdBmwOlTKDljg00eYqLTQO66FPzSTWMc2EMGCae7sVr/OluTg/T4NKFt39gySNurVvPtlXZfqCo3GfCiyTV6iZWeuVMh69PrrozqgCX0mHJ+OyzMtQrTbqUB4BvHZe9Bfo/uyBDmRDWV0vTCz1mz0t+DTOjRkjEiAOFOKSQ5w/L3RgIwmuEgW3kqaQqtwAFIfWb9PxNuLvTLGMttZ3yO5P3aYl9G6jCSrrcr+3m0ICKOTBu8lH/lonRkZOq/08lpP5VtCEak6I+aSIT9tP9LJIZACn/IUe7qE88kjETKmnZT6F1D/1p58pEA0NI4g5CtdHlSXmg0s+zhAKS7tYpvNx96EPw5cCc5+VneGb0RDNvLaa+cEF4M/JuU0PcA9u9gu+PC+byS52tGqNA8yuH7El6JwFI8dXUvX07iAkC2VOvtt4kg0aeiHDyPHJpvvN4TaAH9Bz+WT5FDWNTAz4LC79GO6pQb9j5iojBlt+UUHvr8nfZN6AKa57RMsFTt9m0t0eBVUqR5fgpE/k6+57U9FtAQPZ5ufj66n0Ys1Chyr93K5jhX3GM64JjdryhghfffO150Q+hYrX3a5/fo2ULWBM27UoViPGVCFtmd0Yw1V5F+l8j58Mck1yUYxpU6tg+o1tara6THtW91V2dqC0+ha42qUVZhScMys1ygeqrpwVTvfhsaVH3/e0xXB7cO4UYkBg1ivB9O+90jwFfg1noBWOg7JpyGvPzYuLPz1CzNtVCqtRpqhMbCu4e2xQ++w8gJGD87TjODSjvgsXoDOs/Fs2qzhSatxvKrnW6pmKqwo9j4B12XZ4Sc+4oE2DIquGY8iyYrp9oBkSCQ8kOIkYVD74yj5C+Y/+JkFNVPwwBvarswkuyZUp8gjHCBLFkf0l+yBDWvJ/jZBXyUFSCGDIrpl1USocwndJFH5zst9/ZyaiKGKEO2nEBAuOCo1XTAyPLIjonN2pH7c01ySgFXymnEV0K0UGq78eDfUtxpmcGLtK+75NVraVGD2wNVNrpWJl1al+s+CM4OvabLcM6VnweXcGciDFRmghhWVoE4EqnhFUuFxCB3umtoyn8lKuEy1fmrRsweDOMtUNd0qA6IctHwIM0AOX2Sx0KxqjEhpp+YkfStkyLrzC33yJbUqRbgkDGq1fKfJDAdenpfQOVj6VMCsB208bbzJUcGOWzZtvfnETOnRLxb4LddrcPuP91CawvOVuAphNrIEUsiRon1SrCuL8GVF75tbSHcskqjIVLfycIZlvVjlywu9gBptiORxw/e1CZ7bDeKlTTIK67KQqosSEs1fnc/X0aAxlkqaOEZQdefKhrABuZFa/KTPRhQsFSncg6wI+niscy0rjfkkvg5fe4c17WCpa0eXot7t+4ot9O5+v0H/buYYniE4MzfrsDnJhqu1tLt1z0dNQ60Qz/8RxR7461d9KxJaNTelFLXDQwDHcTCBSk+0BrJVKT9Ls0bHgxr0zDoaDnbnlXjuu9+I+TH6sZYee1kDBqfPV/RKaXBx6yCFxEBosyCqvwmiuHUzItjvCMSpgREhM861FtvcyaGbN1+nFgM0NlPJQdpqz7bpEJcVw8HFp0yAAT61uYy8m51btG5zFKE74t+qEpjkQPOxPzxh52MDHVgMT0vIQcdA2GGXmjLInOlKHy44blBXKhSsvnWk6goe3xaY/vatI9iOJP0zdmqYuV/Z82spbMuwMwDVEEqrn/KPXqWl0G9AIAPPSA/DO5U9NZAn8nW5CcnB359CkSxVmBXbPBph/GvVrjZEiohjaAfRzdYgSBArwPcIhmfsE3ankfWrXOiw0qJgH4UvOuQphVkNCTIDl405MQMo+6Usm6YMkKx93V+wFSt0l6zoNYeELrp5hNwWNc35EVD0YJegiTIgVDqJykV3YM5po2UCDF4a1Ijhgu+mWL/+B3K8OcvmsGG8X/tKBCNPK/0jJT6PKfks/NEJDkcRcfm1ZDp9AFzldq53UZoT4o4zhRSpLA+f6VTIJx4/t78vpyZKMEJmc8RbIp/swFrbSGInwW4NCrovIK+oS5Z3zXeNbGSpuf2oWYAtpQvttaM2LNl4svcEwxvYor7JMy46l1f2SB0Q0PXLIehirHvMLhbfdWLQw0QB7Gq2O0khxvT1LjZ+H+euX7uZmkY9IvXdW0pnDhaNmZKT6nKj9K1bcLT3520W7lrdOzlEMHxtoSMMd9u2LtEkdtO0KIyfVvkXReY+ilkTyBUmcRCEWl27pABXdcl9jZn6A/16Ze1Lv9SFRncN42vpbOS3xkIBPtFwaDftP6IZLtchcxmj3xkeJFH8fFKg5f06HvCjPbxR3US46FTJqo49yM0H1L8wOjSC8wYHb4Mo6Zhh4i48snY9IOVfrIGqFfTsTQ5kxIctBPqGnMO7dl+iu4TUqeHkDk2IkmZSNjB7hp0mmLHKcTAB49JQDsZdlPlcOeADP/r7q/I5vXE8ZHzXqFmxW9v90+JMckU0V0AIrcJK9IQWl4LQR+dRuKRxJwDpy4wa4ymhqnBdjDMqQ/cetUExuVkzntiCPyOz6dMpAx9ZeidxQ02hYjPVqgFg8sCl1lTHTulvk7Nj698usBJMG+IKJorZp7+a97Tr226dW1h++Ic3ERIIDuFrJVY0UvO/vrTZrxZbzT2Ki+UvjN5Ins+P6gU7XLKlAlh4h3u54VXMJO6MqqpSFKXQlRY2fOOn/m5YDfOCvjmhsmrp63Wz9s+kowNsciO+DZa5Mce5qH9/ysvEHv7Sgb3AIZ4+zl1R9px1bU2HI/tcieQUvHkNG0N43uBelEbsrZTfVDAsk7KashZp+QG9k91BWuxlN00Hmaqd3foNx2EwoBe14MbFyJKr0PLJvFrMBQamhlWX31hknK3y9m7F3cIopvO2kIngxuVgZ/c3XOMnJysZcmgeVvouinM2GCcJF5k54InnSO0JJ0g4taICxSdD1NbXw4aVfuPXY2loCOKwXAsHW+vRvIu5yBYsAXeOX1J7LwWwVHOTLjQDRyIwgAsot1J4dr3tRO1u3s72SospfgKrMJdMYtrSJ6zvRQTEDXZcyk3fqtElG55syIjePTyPVPDGCGHVvaqOCWvYDXnsFAy9L3gVg8HaLMerTRuSzj6HjRmyZNheBBZkDOTRmc6yaJVhK/+NCpXgPsW3xyAX6ZGQ44NOAyn9U49Jz5VIUpEfXTK/hDaJeMgl/HmLcfxbBara5U+J5xi9IvwTcMMzxxN/sm/BjLc+34gP33ChIncbfHleQbbQvS6JMkySTA2PCbI/vwYonIZnymVtA3c4fC5zso+ZgTyvnxZkeJdDRPjTUtP6DFIAxMbIotg2e93CXfUp4ciADmTWa4IbuP3n602bqsqzTldZAt7UzolvY0gnTcmZWJC8dCoZhebkdcf9hd+jW/HdVo/YM6s39d1Mqm7PnG2dsXFSCn+yg1redbnDTPpUVi1+T1xd6dGeM7GddroA/qyNLl9dvdvCUGQvRL7BIFQFUZYXRdx27OAStt+iqORvuibZWfLufrRJVM6AoyJNpRo4rALSdtAcfW8d4HJGPEaP1cxl6ErnQz+yDbv+zRMTFCJiuPTJRDXD+ir8hz+eChUN323YpgVJ0Qjl9oqEj9H3SKORfnFaq0337C3oyz0eQ5PedG/d78nJzRP+BfQIOFMDzPSJ40yg+MAgX0P6ZPOiBIW7c/i2j6TQhVyeEUzsjRMYMMiGQl/lgTz9D6Kc/WP4tzbzhRb0Icoy5+sZRiap1rQFjaOVzGUEOXgMoME9voaumyWcTskYTxGdil9CvKBKsHCFx8iZ63V1xcmT2JnOVuYEAqOwD6bSc6KhJznv+nSyG7HNY+ycCXP1NBoG5Z8QgXEcJxUMl0SDUaMAqM4K/NL+ZiQHDbDL38U9eBa9zYaG7xronBtZ7ieC2yMOcMfz4tSvATwPeH+qlTOJQjBtFEzHkFV84bUdVYLaMj8/oM+rVU/4hZCpXR42AXjhfEZBT2M4YZv9ciCjNAo63zbfTv2zt7A6ZYVUkRFW3mRQw0EP7bmK8w4BcVzhy2U0zaJqlBAbc1i/4A+0lmSnyKBISJRF4lrGz1dIsCpZ5AeuDopJNc59Rb7viBjmnA5rBqdrxPhNnReYbJd2k3g7YPAV21Hx4wf7oUsVn8Mu6dgmChDCc1IEc9jxSnHYCWqlCA7YBeUtXTXIJf2qe7knGliksYKnYfX9RnXdeDoIbmKWGsV2mnK+oJPzOlF46TC391bf9GBe8T2rvcXJINCfZBmS60iO+5Yo2NNJQi+Qc9SebaaygxTZOj6rIbNwzdhDEUYCG8zfS9KmEhZKfcz5+9oCIG6mM8oh7q79yxzDIzdpaotBKCgJ9M8jtC/Ee5ZI8adPdXMkB1EEzaGWZBuBvzecpPmTyhzpKBy8FB0kKhEOjY0/utP7JAJKpId0xWuDDsFlSsbCqPgb4wbUqID7Qxu6FUJ1QGCxGYA+u/NXFQesgGrYlWKdm0zY62gtlUv89zV1PwQwB4TNtP16MrfZAuYhqgR2xJ7ON7tWJ49lVyjB5NbzlCGelLKJIkoicwMz1CSQ8b9SO2qk+WMWUPnXqCsHBSU7ews5rZ8ccw539tfEBj9UNPUqW30tjb9BIc5q0ypPa15S8ucZOGEpSGyRLaf8SdSxw1JDsq0vYF04PoWvvYyAIAVNl6ACzWEnCPSzVAb2orLKO2McQpRAY4I762BRDhBt0R6a1Qm9Hx9g0gUfQE6iXBniPe81OUTKzGHNKxHzV2sP3HgVlBmB2M3N2tJTzb65XnRGKLGOgMe2/eVvLj54lK4MRe5vTJG1QvZUKbxnK0YdMNE/N/eTPwJ3tB7tMyVVVDEUQpzKNtWqrbKvtQcxG1Dy42DjnsCW+DNlXdgmIKcG8ZpJT9vTihoR2UAK1ZG1WPhVF2oNNvQGU3z3hIQ8VNmdu0EMJlEu6v4iTlLYi3E68RpLs8Eq1d6csi6nKrJRssSwsm8ApR/yO/p9c7dYj4EsfcwhxzsfgLdpu8SKZUUgHkSs+KWA2F3fHUawrHUZvl4xdkDqC/S4vi8CweW7ed/VvuriZXHgljCahrwhe2YRn0rZl3Kvsc3wz2L8XaRhusY1lT5Xy8rqsCiKFcuevI7DUCV2/c3uuhY08+5+qTihQwGlrJTQo8iTNr39o6lcoalqyKYeXWoQEKpUQP/SvTT5qhq+7NdJoB+q9JkU+q0aEQwqBOF+rdmRUeYEMWXmPiJ7NndcQGuAJg+M5pnbB25DUv2zP2Xqj/PjYypAJMMavI7YgoIlZ6VZ/L1yqU+PlABLp7+A93JgpG0hv221lEPIWY4+RNr3yyhPnCxtGA8obgUDu/6FIHqq+hxm+GfZx2DI2TQjgQs5yJiUyIVoXbmjjoBX0axEn1x3xsa7YlGVeFw1jeqFbgdIFN+KInG4kpJVd07c4BLJiITZFodHExoFD65tsX1SLXpZgdoljKwDo2DkacLCLiaV8PShqJEjo58uXdCu676mtSePbGyW0KZigAPGEpUEZ6zc1l9cZXjeDi2aLJpl6sphMR/B5aiIz6J7Afj3feUuq5qxxFHQC8jR1C1hPV7ZxF7Sub+U5iB+ynvUkt4iJd7kxJDARVbZPBbUSb9/ny0nBbzZmkRE6oi+0ocWxaH4ZnVrsL/NgnFPwKuG2IwbNCHls26kUeON7qS/+j0PLAXzBghwiRgBku1clT/tM30AS1mvJ6cKDjjLPMei7GwGHaJFfQqEjjikb7ktX5O1jVMlZTrNGliwOK1fTh3jE9b5K9AppT5IFuPxhbJ97+HMazBEPtMA9aZBIKXNFIvdPPCs0DHt05HzygjrejibsBA/SS2F+gSlANRlkrJinMIpt/gdlvUbjaxFrMupGmVCoMDfRDrxO053FTh8nto2pA2ActBghuqLM8p91U5FtVhXU+FI8whYX5WdWMmWc2E2wGzFz1aCKYJIC/qr4xzN305xQLxAVb2n0BQedGI+j38cc0ECk1NxJ2isVKvmhk5RyzSc6EPzB1884xko7roUM7NOu0FiPw+Zu4R8OGoHRYqsigkTRxlmL19aGEbBbdK9TmGBvwCd307SHj2GojSWN7DL9olp1+VMMYQ9UG8DTX47r23qkXZ4z3ctQl86rRjpzdj+70XvZb+h0FzgnyJmYSHxIIn2FWNYmvwPjyiBUgHYP5RoHhSJoeI6W+nkFnHijreTncsonIU5FKlqHQFGzzdc8s9U5sfrMFtR1SUYFYWj3C8KP0oQwiXZcn3AcqPkTqVU0o5kRZ2+QS+fJP1ozNeh6hKJSpUVSb2LZ9329cfBOPAJ7u8zYUqJZ8CIzIa26Qy5ADf5bco2Z18IcLHAulDYBXxaBCm2DXpryNEQMYWmMTHA0mVpIFVkmU5dfnNQykdZiAXU1l+Fw6kIjrMJ9AgF0xWiaZnOyTehWtuxU47hvUm8B2A9ociq2x5aFOxazc3YG5IB7IZmXercFhEWIMzMw63jvREmRjCT5ou+MIjmbi1na8d0SaLUudX5pUouPbc+4stjuNveU6cNACO0s+nbAlVyZyCeRMAPk5C+11kHcwSNd8IZugXSih5eJ4xPoIW0knz0365CjhNUfz9+31qYzK0lZNMUCuf2K0vrUBB/i3T3gdXMGSeldKp3Lx+tz/bpKXTHtUzzsvdS9Gs+uMIZ1XK6AxFyeCxOJ+cU9XN1fBnLPe2JYUlJUmCu4tiwsprlamaRzZQNWlUxombEZeKC7q3mwHcZM5wU0ICwEnLfTxW0VL9N10+batqOKxQnIspanPsw1ez2cuwr/hQSPXqoP2gIkFZnmAqUKUX8GZ5ib+C60pulz4Uxz/QvZW7V2SAAGcUwS30VsW6U2Ld2v5UbOfEQCxPdOHJZw75sKgEdyVdN1FDl4JC6s8IUclP+LD6R/CXIEDhbSWuXdTsAinSZLlMH1LzCXp6Cqvih/NReD6FJezE4Hi0sUGxti+4YngNBTWhUOblVY4+ioJs/kpVyXoAksKXh+Fe1j1PG2gbHkCQQWWCDqufQCEypj+dCoj37UreY26CogoUkVCnNUXQ5jZNFOPeXjh336gUEGzTt9qLgRwsxEJpQKH+aCWZALuJHtCVlK1WQMM6eM15EjMtRabejRb7eD3Us4WqESLYxpZ5KCobtmQDzV/4vOlvq0BSClPNORXWKygxQ2J9casayyd9DxvL77P41vt3k3fsT5PB1d6WR+6JZWwYJGZTdxyDyiFJDCKV9TuCeGkZQ26g1V0sV/H5a1xciwxOCNt7GgQOajs3aR4wpXxg4GbU0nOR0c9Ii/Sn27VMt4BqnAj5W4fx8q4ecJlPHlG3tSjqKSUsP0rlyg7JRFXcxCUGv7QMYc2K9WLvLEHbBOcM/ZD87o+UaQ3CvTwOkQTDq8hUeOBRxcerQV5Xi6Y+Hh6Vg4aeMpoGdUV7xXbw5oVh/mkSLP70aWsGQ3UbqZLFHrxQzLeDFkYJX6q069Lp/1X+lGTY+5ykXDRtK1n+GarP5tNWi4nd81eFXdracJWwcYk2GA6MbdjMnoaTrfSHXO3EXgrlq6ko5DABSrMg+9kF88aW5LAVOxGADYFS8bniGvdKVXnEhhQDJVCYKqqWKYGpAek5BGeVRWSbwLCKdQ5BcBnn+oEsmp46uK3k8KO72Pn+1hPMbgE6xWxVYPqAe7HVPPjNRiQS6cQGOxU1gdlAuEJ4V7ip4o+TgDM2/M4bthC6c4SBMQaMfRZfL5ko/uf3U2MXch54RJ2/LQRAy3AHiOI6enjY+L88VIvjU+hnmwro8yEflSD4tEMeFIkrxEW19Gycl1BDXpDVbs9nrU5MMIGx6QxCFw8FibHOtcRcI71o8s+OvDCQFsw7ZVMslGVDaprGZZmJ2j4uTgxrn15ihGv020yixBNktFCYgTyPlxA1f36ciarunxld8CPUVUPV/D/XFX5s/Neg2cdPqmSlO/fpnXxz4UJnIlB6hSl82wNGKJud1KoVyDHmmjI+EKBSUO7kNuvrQ/fY3duE75BX/HUAeUiLFKBZ1O2/mThw8t0Wq782ApG12/Jvza+94ENybWDDpLLmTddfEP7cYjFtZZONpGuxNkP8FAAD//w==" diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 3ceea02d892..029cf8b10e7 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -4,99 +4,221 @@ *--------------------------------------------------------------------------------------------*/ import { isStandalone } from 'vs/base/browser/browser'; -import { parse } from 'vs/base/common/marshalling'; +import { VSBuffer, decodeBase64, encodeBase64 } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { parse } from 'vs/base/common/marshalling'; import { Schemas } from 'vs/base/common/network'; +import { posix } from 'vs/base/common/path'; import { isEqual } from 'vs/base/common/resources'; +import { ltrim } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; import product from 'vs/platform/product/common/product'; +import { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window'; -import { create } from 'vs/workbench/workbench.web.main'; -import { posix } from 'vs/base/common/path'; -import { ltrim } from 'vs/base/common/strings'; -import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api'; import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; -import { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets'; -import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; +import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; +import { create } from 'vs/workbench/workbench.web.main'; -class LocalStorageSecretStorageProvider implements ISecretStorageProvider { - private static readonly STORAGE_KEY = 'secrets.provider'; +interface ISecretStorageCrypto { + seal(data: string): Promise; + unseal(data: string): Promise; +} - private _secrets: Record | undefined; +class TransparentCrypto implements ISecretStorageCrypto { + async seal(data: string): Promise { + return data; + } + + async unseal(data: string): Promise { + return data; + } +} + +const enum AESConstants { + ALGORITHM = 'AES-GCM', + KEY_LENGTH = 256, + IV_LENGTH = 12, +} + +class ServerKeyedAESCrypto implements ISecretStorageCrypto { + private _serverKey: Uint8Array | undefined; + + /** Gets whether the algorithm is supported; requires a secure context */ + public static supported() { + return !!crypto.subtle; + } + + constructor(private readonly authEndpoint: string) { } + + async seal(data: string): Promise { + // Get a new key and IV on every change, to avoid the risk of reusing the same key and IV pair with AES-GCM + // (see also: https://developer.mozilla.org/en-US/docs/Web/API/AesGcmParams#properties) + const iv = window.crypto.getRandomValues(new Uint8Array(AESConstants.IV_LENGTH)); + // crypto.getRandomValues isn't a good-enough PRNG to generate crypto keys, so we need to use crypto.subtle.generateKey and export the key instead + const clientKeyObj = await window.crypto.subtle.generateKey( + { name: AESConstants.ALGORITHM as const, length: AESConstants.KEY_LENGTH as const }, + true, + ['encrypt', 'decrypt'] + ); + + const clientKey = new Uint8Array(await window.crypto.subtle.exportKey('raw', clientKeyObj)); + const key = await this.getKey(clientKey); + const dataUint8Array = new TextEncoder().encode(data); + const cipherText: ArrayBuffer = await window.crypto.subtle.encrypt( + { name: AESConstants.ALGORITHM as const, iv }, + key, + dataUint8Array + ); + + // Base64 encode the result and store the ciphertext, the key, and the IV in localStorage + // Note that the clientKey and IV don't need to be secret + const result = new Uint8Array([...clientKey, ...iv, ...new Uint8Array(cipherText)]); + return encodeBase64(VSBuffer.wrap(result)); + } + + async unseal(data: string): Promise { + // encrypted should contain, in order: the key (32-byte), the IV for AES-GCM (12-byte) and the ciphertext (which has the GCM auth tag at the end) + // Minimum length must be 44 (key+IV length) + 16 bytes (1 block encrypted with AES - regardless of key size) + const dataUint8Array = decodeBase64(data); + + if (dataUint8Array.byteLength < 60) { + throw Error('Invalid length for the value for credentials.crypto'); + } + + const keyLength = AESConstants.KEY_LENGTH / 8; + const clientKey = dataUint8Array.slice(0, keyLength); + const iv = dataUint8Array.slice(keyLength, keyLength + AESConstants.IV_LENGTH); + const cipherText = dataUint8Array.slice(keyLength + AESConstants.IV_LENGTH); + + // Do the decryption and parse the result as JSON + const key = await this.getKey(clientKey.buffer); + const decrypted = await window.crypto.subtle.decrypt( + { name: AESConstants.ALGORITHM as const, iv: iv.buffer }, + key, + cipherText.buffer + ); + + return new TextDecoder().decode(new Uint8Array(decrypted)); + } + + /** + * Given a clientKey, returns the CryptoKey object that is used to encrypt/decrypt the data. + * The actual key is (clientKey XOR serverKey) + */ + private async getKey(clientKey: Uint8Array): Promise { + if (!clientKey || clientKey.byteLength !== AESConstants.KEY_LENGTH / 8) { + throw Error('Invalid length for clientKey'); + } + + const serverKey = await this.getServerKeyPart(); + const keyData = new Uint8Array(AESConstants.KEY_LENGTH / 8); + + for (let i = 0; i < keyData.byteLength; i++) { + keyData[i] = clientKey[i]! ^ serverKey[i]!; + } + + return window.crypto.subtle.importKey( + 'raw', + keyData, + { + name: AESConstants.ALGORITHM as const, + length: AESConstants.KEY_LENGTH as const, + }, + true, + ['encrypt', 'decrypt'] + ); + } + + private async getServerKeyPart(): Promise { + if (this._serverKey) { + return this._serverKey; + } + + let attempt = 0; + let lastError: unknown | undefined; + + while (attempt <= 3) { + try { + const res = await fetch(this.authEndpoint, { credentials: 'include', method: 'POST' }); + if (!res.ok) { + throw new Error(res.statusText); + } + const serverKey = new Uint8Array(await await res.arrayBuffer()); + if (serverKey.byteLength !== AESConstants.KEY_LENGTH / 8) { + throw Error(`The key retrieved by the server is not ${AESConstants.KEY_LENGTH} bit long.`); + } + this._serverKey = serverKey; + return this._serverKey; + } catch (e) { + lastError = e; + attempt++; + + // exponential backoff + await new Promise(resolve => setTimeout(resolve, attempt * attempt * 100)); + } + } + + throw lastError; + } +} + +export class LocalStorageSecretStorageProvider implements ISecretStorageProvider { + private readonly _storageKey = 'secrets.provider'; + + private _secretsPromise: Promise> = this.load(); type: 'in-memory' | 'persisted' | 'unknown' = 'persisted'; - constructor() { - let authSessionInfo: (AuthenticationSessionInfo & { scopes: string[][] }) | undefined; - const authSessionElement = document.getElementById('vscode-workbench-auth-session'); - const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined; - if (authSessionElementAttribute) { + constructor( + private readonly crypto: ISecretStorageCrypto, + ) { } + + private async load(): Promise> { + // Get the secrets from localStorage + const encrypted = window.localStorage.getItem(this._storageKey); + if (encrypted) { try { - authSessionInfo = JSON.parse(authSessionElementAttribute); - } catch (error) { /* Invalid session is passed. Ignore. */ } - } - - if (authSessionInfo) { - // Settings Sync Entry - this.set(`${product.urlProtocol}.loginAccount`, JSON.stringify(authSessionInfo)); - - // Auth extension Entry - if (authSessionInfo.providerId !== 'github') { - console.error(`Unexpected auth provider: ${authSessionInfo.providerId}. Expected 'github'.`); - return; + return JSON.parse(await this.crypto.unseal(encrypted)); + } catch (err) { + // TODO: send telemetry + console.error('Failed to decrypt secrets from localStorage', err); + window.localStorage.removeItem(this._storageKey); } - const authAccount = JSON.stringify({ extensionId: 'vscode.github-authentication', key: 'github.auth' }); - this.set(authAccount, JSON.stringify(authSessionInfo.scopes.map(scopes => ({ - id: authSessionInfo!.id, - scopes, - accessToken: authSessionInfo!.accessToken - })))); } + + return {}; } - get(key: string): Promise { - return Promise.resolve(this.secrets[key]); + async get(key: string): Promise { + const secrets = await this._secretsPromise; + return secrets[key]; } - set(key: string, value: string): Promise { - this.secrets[key] = value; + async set(key: string, value: string): Promise { + const secrets = await this._secretsPromise; + secrets[key] = value; + this._secretsPromise = Promise.resolve(secrets); this.save(); - - return Promise.resolve(); } async delete(key: string): Promise { - delete this.secrets[key]; - + const secrets = await this._secretsPromise; + delete secrets[key]; + this._secretsPromise = Promise.resolve(secrets); this.save(); - - return Promise.resolve(); } - private get secrets(): Record { - if (!this._secrets) { - try { - const serializedCredentials = window.localStorage.getItem(LocalStorageSecretStorageProvider.STORAGE_KEY); - if (serializedCredentials) { - this._secrets = JSON.parse(serializedCredentials); - } - } catch (error) { - // ignore - } - - if (!(this._secrets instanceof Object)) { - this._secrets = {}; - } + private async save(): Promise { + try { + const encrypted = await this.crypto.seal(JSON.stringify(await this._secretsPromise)); + window.localStorage.setItem(this._storageKey, encrypted); + } catch (err) { + console.error(err); } - - return this._secrets; - } - - private save(): void { - window.localStorage.setItem(LocalStorageSecretStorageProvider.STORAGE_KEY, JSON.stringify(this.secrets)); } } + class LocalStorageURLCallbackProvider extends Disposable implements IURLCallbackProvider { private static REQUEST_ID = 0; @@ -390,6 +512,17 @@ class WorkspaceProvider implements IWorkspaceProvider { } } +function readCookie(name: string): string | undefined { + const cookies = document.cookie.split('; '); + for (const cookie of cookies) { + if (cookie.startsWith(name + '=')) { + return cookie.substring(name.length + 1); + } + } + + return undefined; +} + (function () { // Find config by checking for DOM @@ -399,6 +532,9 @@ class WorkspaceProvider implements IWorkspaceProvider { throw new Error('Missing web configuration element'); } const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents; workspaceUri?: UriComponents; callbackRoute: string } = JSON.parse(configElementAttribute); + const secretStorageKeyPath = readCookie('vscode-secret-key-path'); + const secretStorageCrypto = secretStorageKeyPath && ServerKeyedAESCrypto.supported() + ? new ServerKeyedAESCrypto(secretStorageKeyPath) : new TransparentCrypto(); // Create workbench create(document.body, { @@ -407,6 +543,8 @@ class WorkspaceProvider implements IWorkspaceProvider { settingsSyncOptions: config.settingsSyncOptions ? { enabled: config.settingsSyncOptions.enabled, } : undefined, workspaceProvider: WorkspaceProvider.create(config), urlCallbackProvider: new LocalStorageURLCallbackProvider(config.callbackRoute), - secretStorageProvider: config.remoteAuthority ? undefined /* with a remote, we don't use a local secret storage provider */ : new LocalStorageSecretStorageProvider() + secretStorageProvider: config.remoteAuthority && !secretStorageKeyPath + ? undefined /* with a remote without embedder-preferred storage, store on the remote */ + : new LocalStorageSecretStorageProvider(secretStorageCrypto), }); })(); From 47fac5e7c05a16200fb0d774dde7ed398449c695 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 28 Aug 2023 19:13:58 -0700 Subject: [PATCH 301/607] A few tests for Related Information (#191547) --- .../aiRelatedInformationService.test.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts diff --git a/src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts b/src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts new file mode 100644 index 00000000000..6d521ca8777 --- /dev/null +++ b/src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { AiRelatedInformationService } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService'; +import { NullLogService } from 'vs/platform/log/common/log'; +import { CommandInformationResult, IAiRelatedInformationProvider, RelatedInformationType } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; +import { CancellationToken } from 'vs/base/common/cancellation'; + +suite('AiRelatedInformationService', () => { + let service: AiRelatedInformationService; + + setup(() => { + service = new AiRelatedInformationService(new NullLogService()); + }); + + test('should check if providers are registered', () => { + assert.equal(service.isEnabled(), false); + service.registerAiRelatedInformationProvider(RelatedInformationType.CommandInformation, { provideAiRelatedInformation: () => Promise.resolve([]) }); + assert.equal(service.isEnabled(), true); + }); + + test('should register and unregister providers', () => { + const provider: IAiRelatedInformationProvider = { provideAiRelatedInformation: () => Promise.resolve([]) }; + const disposable = service.registerAiRelatedInformationProvider(RelatedInformationType.CommandInformation, provider); + assert.strictEqual(service.isEnabled(), true); + disposable.dispose(); + assert.strictEqual(service.isEnabled(), false); + }); + + test('should get related information', async () => { + const command = 'command'; + const provider: IAiRelatedInformationProvider = { + provideAiRelatedInformation: () => Promise.resolve([{ type: RelatedInformationType.CommandInformation, command, weight: 1 }]) + }; + service.registerAiRelatedInformationProvider(RelatedInformationType.CommandInformation, provider); + const result = await service.getRelatedInformation('query', [RelatedInformationType.CommandInformation], CancellationToken.None); + assert.strictEqual(result.length, 1); + assert.strictEqual((result[0] as CommandInformationResult).command, command); + }); +}); From ebd67244fb2da33ab078bb2baa96106fda29f336 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 28 Aug 2023 19:14:10 -0700 Subject: [PATCH 302/607] Turn on Command Center by default (#191550) * Turn on Command Center by default Fixes https://github.com/microsoft/vscode/issues/191549 * remove tag --- src/vs/workbench/browser/workbench.contribution.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 5b1d769a4a4..e41769f836d 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -583,8 +583,7 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'window.commandCenter': { type: 'boolean', - default: false, - tags: ['experimental'], + default: true, markdownDescription: isWeb ? localize('window.commandCenterWeb', "Show command launcher together with the window title.") : localize({ key: 'window.commandCenter', comment: ['{0} is a placeholder for a setting identifier.'] }, "Show command launcher together with the window title. This setting only has an effect when {0} is set to {1}.", '`#window.titleBarStyle#`', '`custom`') From fdb265d2214c933d18d6e75a44db4d9223703521 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 29 Aug 2023 05:52:08 -0700 Subject: [PATCH 303/607] Add GNU style link 'r-c.ce', 'r.c-re.ce' See https://www.gnu.org/prep/standards/html_node/Errors.html sourcefile:line1.column1-line2.column2: message sourcefile:line1.column1-column2: message sourcefile:line1-line2: message Fixes #190350 --- .../links/browser/terminalLinkParsing.ts | 12 ++++++++---- .../links/test/browser/terminalLinkParsing.test.ts | 2 ++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts index 1e8b5bec80f..0c1a3dd16a0 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing.ts @@ -63,9 +63,11 @@ function generateLinkSuffixRegex(eolOnly: boolean) { // The comments in the regex below use real strings/numbers for better readability, here's // the legend: - // - Path = foo - // - Row = 339 - // - Col = 12 + // - Path = foo + // - Row = 339 + // - Col = 12 + // - RowEnd = 341 + // - ColEnd = 14 // // These all support single quote ' in the place of " and [] in the place of () const lineAndColumnRegexClauses = [ @@ -78,7 +80,9 @@ function generateLinkSuffixRegex(eolOnly: boolean) { // "foo",339 // "foo",339:12 // "foo",339.12 - `(?::| |['"],)${r()}([:.]${c()})?` + eolSuffix, + // "foo",339.12-14 + // "foo",339.12-341.14 + `(?::| |['"],)${r()}([:.]${c()}(?:-(?:${re()}\.)?${ce()})?)?` + eolSuffix, // The quotes below are optional [#171652] // "foo", line 339 [#40468] // "foo", line 339, col 12 diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts index c200c575208..453b1f95ec4 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts @@ -47,6 +47,8 @@ const testLinks: ITestLink[] = [ { link: 'foo 339', prefix: undefined, suffix: ' 339', hasRow: true, hasCol: false }, { link: 'foo 339:12', prefix: undefined, suffix: ' 339:12', hasRow: true, hasCol: true }, { link: 'foo 339.12', prefix: undefined, suffix: ' 339.12', hasRow: true, hasCol: true }, + { link: 'foo 339.12-14', prefix: undefined, suffix: ' 339.12-14', hasRow: true, hasCol: true, hasRowEnd: false, hasColEnd: true }, + { link: 'foo 339.12-341.14', prefix: undefined, suffix: ' 339.12-341.14', hasRow: true, hasCol: true, hasRowEnd: true, hasColEnd: true }, // Double quotes { link: '"foo",339', prefix: '"', suffix: '",339', hasRow: true, hasCol: false }, From 24946e782463e5bd32601e5a0dfe32b3d97d39be Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 29 Aug 2023 15:13:28 +0200 Subject: [PATCH 304/607] fix #191374 --- .../environment/electron-main/environmentMainService.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 748ff075783..aab03b3130f 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -6,6 +6,7 @@ import { memoize } from 'vs/base/common/decorators'; import { join } from 'vs/base/common/path'; import { isLinux } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; import { createStaticIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; @@ -68,6 +69,9 @@ export class EnvironmentMainService extends NativeEnvironmentService implements @memoize get useCodeCache(): boolean { return !!this.codeCachePath; } + @memoize + override get userRoamingDataHome(): URI { return this.appSettingsHome; } + unsetSnapExportedVariables() { if (!isLinux) { return; From fbd61d106cd10077bd941abbd8a55af6858200ef Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 30 Aug 2023 00:14:02 +0900 Subject: [PATCH 305/607] chore: update distro (#191633) --- package.json | 2 +- remote/.yarnrc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4dbaa275413..276a62f924f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "49cc0fbc0a8e222bcce2a7c3bf62e0d23f20d258", + "distro": "2100ad274ed566f978bb917f327b3d99f95d59f2", "author": { "name": "Microsoft Corporation" }, diff --git a/remote/.yarnrc b/remote/.yarnrc index c4421581246..26dc815d0f8 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,5 +1,5 @@ disturl "https://nodejs.org/dist" target "18.15.0" -ms_build_id "223745" +ms_build_id "229541" runtime "node" build_from_source "true" From 72d5c4d06c322e9d2602337cde5e7fea3eb2b90d Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:20:30 -0700 Subject: [PATCH 306/607] quick search - file highlight decoration isn't showing up on search (#191544) Fixes #191539 --- .../quickTextSearch/textSearchQuickAccess.ts | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 6324613fbfd..14b1636c0b8 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; import { IMatch } from 'vs/base/common/filters'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { basenameOrAuthority, dirname } from 'vs/base/common/resources'; import { ThemeIcon } from 'vs/base/common/themables'; import { IRange, Range } from 'vs/editor/common/core/range'; @@ -15,8 +15,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILabelService } from 'vs/platform/label/common/label'; import { WorkbenchCompressibleObjectTree, getSelectionKeyboardEvent } from 'vs/platform/list/browser/listService'; import { FastAndSlowPicks, IPickerQuickAccessItem, PickerQuickAccessProvider, Picks } from 'vs/platform/quickinput/browser/pickerQuickAccess'; -import { DefaultQuickAccessFilterValue } from 'vs/platform/quickinput/common/quickAccess'; -import { IKeyMods, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; +import { DefaultQuickAccessFilterValue, IQuickAccessProviderRunOptions } from 'vs/platform/quickinput/common/quickAccess'; +import { IKeyMods, IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor'; import { IViewsService } from 'vs/workbench/common/views'; @@ -75,6 +75,19 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider, token: CancellationToken, runOptions?: IQuickAccessProviderRunOptions): IDisposable { + const disposables = new DisposableStore(); + disposables.add(super.provide(picker, token, runOptions)); + disposables.add(picker.onDidHide(() => this.searchModel.searchResult.toggleHighlights(false))); + disposables.add(picker.onDidAccept(() => this.searchModel.searchResult.toggleHighlights(false))); + return disposables; + } + private get configuration() { const editorConfig = this._configurationService.getValue().workbench?.editor; const searchConfig = this._configurationService.getValue().search; @@ -247,6 +260,9 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider 0) { + this.searchModel.searchResult.toggleHighlights(true); + } if (matches.length >= MAX_FILES_SHOWN) { return syncResult; @@ -254,9 +270,14 @@ export class TextSearchQuickAccess extends PickerQuickAccessProvider { - return this._getPicksFromMatches(asyncResults, MAX_FILES_SHOWN - matches.length); - }) + additionalPicks: allMatches.asyncResults + .then(asyncResults => this._getPicksFromMatches(asyncResults, MAX_FILES_SHOWN - matches.length)) + .then(picks => { + if (picks.length > 0) { + this.searchModel.searchResult.toggleHighlights(true); + } + return picks; + }) }; } From 4056fc822ab355a7d1603f4f5d701f8863be5cd6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:43:14 -0700 Subject: [PATCH 307/607] Dim settings and keybindings editors Fixes #191612 Fixes #191611 --- .../accessibility/browser/unfocusedViewDimmingContribution.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index 1dc2b4b3605..5012089fda7 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -46,6 +46,10 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .monaco-editor { ${filterRule} }`); // Terminal editors rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .terminal-wrapper { ${filterRule} }`); + // Settings editor + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .settings-editor { ${filterRule} }`); + // Keybindings editor + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .keybindings-editor { ${filterRule} }`); cssTextContent = [...rules].join('\n'); } From a56713bb39e86aa4f0396dc892faecafaf7d4702 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Aug 2023 08:43:17 -0700 Subject: [PATCH 308/607] add onDidRunText --- .../contrib/terminal/browser/terminal.ts | 1 + .../terminal/browser/terminalActions.ts | 11 +---------- .../terminal/browser/terminalInstance.ts | 3 +++ .../terminal.accessibility.contribution.ts | 18 +++++++++++++++--- 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index bba8941c31b..c69c05e99d0 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -591,6 +591,7 @@ export interface ITerminalInstance { onDidBlur: Event; onDidInputData: Event; onDidChangeSelection: Event; + onDidRunText: Event; /** * An event that fires when a terminal is dropped on this instance via drag and drop. diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 38423fb1e4c..2cc1e80827a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -15,7 +15,7 @@ import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EndOfLinePreference } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; -import { CONTEXT_ACCESSIBILITY_MODE_ENABLED, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; import { Action2, registerAction2, IAction2Options } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -549,8 +549,6 @@ export function registerTerminalActions() { title: { value: localize('workbench.action.terminal.runSelectedText', "Run Selected Text In Active Terminal"), original: 'Run Selected Text In Active Terminal' }, run: async (c, accessor) => { const codeEditorService = accessor.get(ICodeEditorService); - const configurationService = accessor.get(IConfigurationService); - const accessibilityService = accessor.get(IAccessibilityService); const editor = codeEditorService.getActiveCodeEditor(); if (!editor || !editor.hasModel()) { return; @@ -566,13 +564,6 @@ export function registerTerminalActions() { } await instance.sendText(text, true, true); await c.service.revealActiveTerminal(); - const focusAfterRun = configurationService.getValue(TerminalSettingId.FocusAfterRun); - const focusTerminal = focusAfterRun === 'terminal' || (focusAfterRun === 'auto' && accessibilityService.isScreenReaderOptimized()); - if (focusTerminal) { - instance.focus(true); - } else if (focusAfterRun === 'accessible-buffer') { - instance.getContribution('terminal.accessible-buffer')?.requestFocus?.(); - } } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 3262f52d621..449e9fce543 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -322,6 +322,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { readonly onRequestAddInstanceToGroup = this._onRequestAddInstanceToGroup.event; private readonly _onDidChangeHasChildProcesses = this._register(new Emitter()); readonly onDidChangeHasChildProcesses = this._onDidChangeHasChildProcesses.event; + private readonly _onDidRunText = this._register(new Emitter()); + readonly onDidRunText = this._onDidRunText.event; constructor( private readonly _terminalShellTypeContextKey: IContextKey, @@ -1255,6 +1257,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._onDidInputData.fire(this); this.xterm?.suggestController?.handleNonXtermData(text); this.xterm?.scrollToBottom(); + this._onDidRunText.fire(); } async sendPath(originalPath: string | URI, addNewLine: boolean): Promise { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 253c0837c09..0bdcf0a7803 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -6,12 +6,13 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; +import { CONTEXT_ACCESSIBILITY_MODE_ENABLED, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickPick, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; -import { terminalTabFocusModeContextKey } from 'vs/platform/terminal/common/terminal'; +import { TerminalSettingId, terminalTabFocusModeContextKey } from 'vs/platform/terminal/common/terminal'; import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -59,9 +60,20 @@ class AccessibleBufferContribution extends DisposableStore implements ITerminalC private readonly _instance: ITerminalInstance, processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationService configurationService: IConfigurationService, + @IAccessibilityService accessibilityService: IAccessibilityService ) { super(); + this.add(_instance.onDidRunText(() => { + const focusAfterRun = configurationService.getValue(TerminalSettingId.FocusAfterRun); + const focusTerminal = focusAfterRun === 'terminal' || (focusAfterRun === 'auto' && accessibilityService.isScreenReaderOptimized()); + if (focusTerminal) { + _instance.focus(true); + } else if (focusAfterRun === 'accessible-buffer') { + _instance.getContribution('terminal.accessible-buffer')?.requestFocus?.(); + } + })); } layout(xterm: IXtermTerminal & { raw: Terminal }): void { this._xterm = xterm; From 406091a9a2a4dd85f32f9dddc0aaa022e15d073c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:47:09 -0700 Subject: [PATCH 309/607] Dim breadcrumbs Fixes #191608 --- .../accessibility/browser/unfocusedViewDimmingContribution.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index 5012089fda7..f507e5b1d4c 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -44,6 +44,8 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor rules.add(`.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper:not(:focus-within) { ${filterRule} }`); // Text editors rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .monaco-editor { ${filterRule} }`); + // Breadcrumbs + rules.add(`.monaco-workbench .editor-group-container:not(.active) .tabs-breadcrumbs { ${filterRule} }`); // Terminal editors rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .terminal-wrapper { ${filterRule} }`); // Settings editor From ea3214b632b2cefe27eb20b20b4996b2e1ba83e1 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Aug 2023 08:47:42 -0700 Subject: [PATCH 310/607] revert some changes --- src/vs/workbench/contrib/terminal/browser/terminal.ts | 1 - src/vs/workbench/contrib/terminal/browser/terminalActions.ts | 4 +--- .../browser/terminal.accessibility.contribution.ts | 5 +---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index c69c05e99d0..4092072afd7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -40,7 +40,6 @@ export const ITerminalInstanceService = createDecorator Date: Tue, 29 Aug 2023 08:50:47 -0700 Subject: [PATCH 311/607] Prefer .editor-group-container over .editor-instance --- .../browser/unfocusedViewDimmingContribution.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index f507e5b1d4c..97e68034743 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -43,15 +43,15 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor // Terminals rules.add(`.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper:not(:focus-within) { ${filterRule} }`); // Text editors - rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .monaco-editor { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-group-container:not(.active) .monaco-editor { ${filterRule} }`); // Breadcrumbs rules.add(`.monaco-workbench .editor-group-container:not(.active) .tabs-breadcrumbs { ${filterRule} }`); // Terminal editors - rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .terminal-wrapper { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-group-container:not(.active) .terminal-wrapper { ${filterRule} }`); // Settings editor - rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .settings-editor { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-group-container:not(.active) .settings-editor { ${filterRule} }`); // Keybindings editor - rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .keybindings-editor { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-group-container:not(.active) .keybindings-editor { ${filterRule} }`); cssTextContent = [...rules].join('\n'); } From caa82e0443e3267ba053139098fe527f2a840b75 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:54:16 -0700 Subject: [PATCH 312/607] Dim editor placeholder Fixes #191614 --- .../accessibility/browser/unfocusedViewDimmingContribution.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index 97e68034743..fade15e0f6e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -36,6 +36,8 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor ); if (opacity !== 1) { + // These filter rules are more specific than may be expected as the `filter` + // rule can cause problems if it's used inside the element like on editor hovers const rules = new Set(); const filterRule = `filter: opacity(${opacity});`; // Terminal tabs @@ -52,6 +54,8 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor rules.add(`.monaco-workbench .editor-group-container:not(.active) .settings-editor { ${filterRule} }`); // Keybindings editor rules.add(`.monaco-workbench .editor-group-container:not(.active) .keybindings-editor { ${filterRule} }`); + // Editor placeholder (error case) + rules.add(`.monaco-workbench .editor-group-container:not(.active) .monaco-editor-pane-placeholder { ${filterRule} }`); cssTextContent = [...rules].join('\n'); } From b37772b7990e9cf699a1c29a1d543d5c86d55818 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 29 Aug 2023 08:55:40 -0700 Subject: [PATCH 313/607] Dim welcome editor Fixes #191613 --- .../accessibility/browser/unfocusedViewDimmingContribution.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index fade15e0f6e..19712c0fa6e 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -56,6 +56,8 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor rules.add(`.monaco-workbench .editor-group-container:not(.active) .keybindings-editor { ${filterRule} }`); // Editor placeholder (error case) rules.add(`.monaco-workbench .editor-group-container:not(.active) .monaco-editor-pane-placeholder { ${filterRule} }`); + // Welcome editor + rules.add(`.monaco-workbench .editor-group-container:not(.active) .gettingStartedContainer { ${filterRule} }`); cssTextContent = [...rules].join('\n'); } From 64f1488b7e388e744cd80cd1bf5b238c9f193b0a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 29 Aug 2023 09:05:38 -0700 Subject: [PATCH 314/607] Dim unfocused setting feedback Fixes #191618 --- .../browser/accessibilityConfiguration.ts | 12 ++++++------ .../browser/unfocusedViewDimmingContribution.ts | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 2cd52bc8311..325a0afea94 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -21,8 +21,8 @@ export const accessibleViewCurrentProviderId = new RawContextKey('access * were better to live under workbench for discoverability. */ export const enum AccessibilityWorkbenchSettingId { - ViewDimUnfocusedEnabled = 'workbench.view.dimUnfocused.enabled', - ViewDimUnfocusedOpacity = 'workbench.view.dimUnfocused.opacity' + DimUnfocusedEnabled = 'accessibility.dimUnfocused.enabled', + DimUnfocusedOpacity = 'accessibility.dimUnfocused.opacity' } export const enum ViewDimUnfocusedOpacityProperties { @@ -120,15 +120,15 @@ export function registerAccessibilityConfiguration() { registry.registerConfiguration({ ...workbenchConfigurationNodeBase, properties: { - [AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled]: { - description: localize('dimUnfocusedEnabled', 'Whether to dim unfocused editors and terminals, making the focused view more obvious.'), + [AccessibilityWorkbenchSettingId.DimUnfocusedEnabled]: { + description: localize('dimUnfocusedEnabled', 'Whether to dim unfocused editors and terminals, which makes it more clear where typed input will go to. This works with the majority of editors with the notable exceptions of those that utilize iframes like notebooks and extension webview editors.'), type: 'boolean', default: false, tags: ['accessibility'], scope: ConfigurationScope.APPLICATION, }, - [AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity]: { - description: localize('dimUnfocusedOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.', `\`#${AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled}#\``), + [AccessibilityWorkbenchSettingId.DimUnfocusedOpacity]: { + description: localize('dimUnfocusedOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.', `\`#${AccessibilityWorkbenchSettingId.DimUnfocusedEnabled}#\``), type: 'number', minimum: ViewDimUnfocusedOpacityProperties.Minimum, maximum: ViewDimUnfocusedOpacityProperties.Maximum, diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index 19712c0fa6e..d4c7a06284c 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -21,16 +21,16 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor this._register(toDisposable(() => this._removeStyleElement())); this._register(Event.runAndSubscribe(configurationService.onDidChangeConfiguration, e => { - if (e && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled) && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity)) { + if (e && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.DimUnfocusedEnabled) && !e.affectsConfiguration(AccessibilityWorkbenchSettingId.DimUnfocusedOpacity)) { return; } let cssTextContent = ''; - const enabled = ensureBoolean(configurationService.getValue(AccessibilityWorkbenchSettingId.ViewDimUnfocusedEnabled), false); + const enabled = ensureBoolean(configurationService.getValue(AccessibilityWorkbenchSettingId.DimUnfocusedEnabled), false); if (enabled) { const opacity = clamp( - ensureNumber(configurationService.getValue(AccessibilityWorkbenchSettingId.ViewDimUnfocusedOpacity), ViewDimUnfocusedOpacityProperties.Default), + ensureNumber(configurationService.getValue(AccessibilityWorkbenchSettingId.DimUnfocusedOpacity), ViewDimUnfocusedOpacityProperties.Default), ViewDimUnfocusedOpacityProperties.Minimum, ViewDimUnfocusedOpacityProperties.Maximum ); From 2af3045474f52bad8f14f01b09acfd5912e7fb5a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 29 Aug 2023 10:28:37 -0700 Subject: [PATCH 315/607] tunnels: fix forwarding attempts wrong path to tunnel binary on linux (#191657) Fixes #191621 --- extensions/tunnel-forwarding/src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/tunnel-forwarding/src/extension.ts b/extensions/tunnel-forwarding/src/extension.ts index 3dd6fdfef19..83789934df5 100644 --- a/extensions/tunnel-forwarding/src/extension.ts +++ b/extensions/tunnel-forwarding/src/extension.ts @@ -25,7 +25,7 @@ const cliPath = process.env.VSCODE_FORWARDING_IS_DEV ? path.join(__dirname, '../../../cli/target/debug/code') : path.join( vscode.env.appRoot, - process.platform === 'win32' ? '../../bin' : 'bin', + process.platform === 'darwin' ? 'bin' : '../../bin', vscode.env.appQuality === 'stable' ? 'code-tunnel' : 'code-tunnel-insiders', ) + (process.platform === 'win32' ? '.exe' : ''); From a76ad82cad11dc5b0019585c61ae30ffa79d523b Mon Sep 17 00:00:00 2001 From: Kartik Raj Date: Tue, 29 Aug 2023 18:13:23 +0000 Subject: [PATCH 316/607] Amend --- src/vscode-dts/vscode.d.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 5a146bee7bb..4f5c61daaa7 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -11457,6 +11457,9 @@ declare module 'vscode' { clear(): void; } + /** + * A collection of mutations that an extension can apply to a process environment. Applies to all scopes. + */ export interface GlobalEnvironmentVariableCollection extends EnvironmentVariableCollection { /** * Gets scope-specific environment variable collection for the extension. This enables alterations to @@ -11471,6 +11474,8 @@ declare module 'vscode' { * If a scope parameter is omitted, collection applicable to all relevant scopes for that parameter is * returned. For instance, if the 'workspaceFolder' parameter is not specified, the collection that applies * across all workspace folders will be returned. + * + * @return Environment variable collection for the passed in scope. */ getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; } From 96ddbc4daad3129d444e9a0b49d5fa86177a8c3f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 29 Aug 2023 21:13:55 +0200 Subject: [PATCH 317/607] Notification and notification buttons lack border radius (fix #191532) (#191557) --- .../browser/parts/notifications/notificationsViewer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 0be6eaf64c0..cb9d9b7ce0e 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -93,7 +93,7 @@ export class NotificationsListDelegate implements IListVirtualDelegate 1 */)}px`; // Render message into offset helper const renderedMessage = NotificationMessageRenderer.render(notification.message); From c63ccaba1c9f6659859645e134f67bc146e62781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 29 Aug 2023 21:32:59 +0200 Subject: [PATCH 318/607] add screencastMode.keyboardOptions.showKeybindings (#191686) fixes #179541 --- src/vs/workbench/browser/actions/developerActions.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 91297a82197..48c3f08a46e 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -94,6 +94,7 @@ class InspectContextKeysAction extends Action2 { interface IScreencastKeyboardOptions { readonly showKeys?: boolean; + readonly showKeybindings?: boolean; readonly showCommands?: boolean; readonly showCommandGroups?: boolean; readonly showSingleEditorCursorMoves?: boolean; @@ -315,7 +316,7 @@ class ToggleScreencastModeAction extends Action2 { append(keyboardMarker, $('span.title', {}, `${commandAndGroupLabel} `)); } - if (options.showKeys ?? true) { + if ((options.showKeys ?? true) || (command && (options.showKeybindings ?? true))) { // Fix label for arrow keys keyLabel = keyLabel?.replace('UpArrow', '↑') ?.replace('DownArrow', '↓') @@ -435,6 +436,11 @@ configurationRegistry.registerConfiguration({ default: true, description: localize('screencastMode.keyboardOptions.showKeys', "Show raw keys.") }, + 'showKeybindings': { + type: 'boolean', + default: true, + description: localize('screencastMode.keyboardOptions.showKeybindings', "Show keyboard shortcuts.") + }, 'showCommands': { type: 'boolean', default: true, @@ -453,6 +459,7 @@ configurationRegistry.registerConfiguration({ }, default: { 'showKeys': true, + 'showKeybindings': true, 'showCommands': true, 'showCommandGroups': false, 'showSingleEditorCursorMoves': true From 350bdb4b1be87cbe4ac2a612d9edcb70297a5a75 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 29 Aug 2023 12:50:28 -0700 Subject: [PATCH 319/607] Fix dim unfocused settings link Fixes #191643 --- .../contrib/accessibility/browser/accessibilityConfiguration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 325a0afea94..05812456635 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -128,7 +128,7 @@ export function registerAccessibilityConfiguration() { scope: ConfigurationScope.APPLICATION, }, [AccessibilityWorkbenchSettingId.DimUnfocusedOpacity]: { - description: localize('dimUnfocusedOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.', `\`#${AccessibilityWorkbenchSettingId.DimUnfocusedEnabled}#\``), + markdownDescription: localize('dimUnfocusedOpacity', 'The opacity fraction (0.2 to 1.0) to use for unfocused editors and terminals. This will only take effect when {0} is enabled.', `\`#${AccessibilityWorkbenchSettingId.DimUnfocusedEnabled}#\``), type: 'number', minimum: ViewDimUnfocusedOpacityProperties.Minimum, maximum: ViewDimUnfocusedOpacityProperties.Maximum, From 1157f145b608edb87b90146536e64d68292990e3 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 29 Aug 2023 13:31:39 -0700 Subject: [PATCH 320/607] Use correct check for just focusing (#191696) fixes https://github.com/microsoft/vscode-internalbacklog/issues/4580 --- src/vs/workbench/contrib/chat/browser/chatQuick.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 03420bc79b4..5545e1a5d03 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -59,8 +59,9 @@ export class QuickChatService extends Disposable implements IQuickChatService { this.open(providerId, query); } } + open(providerId?: string, query?: string | undefined): void { - if (this.focused) { + if (this._input) { return this.focus(); } From 9858757c41ab641b72b68845278b910c6fea8cdc Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:33:29 -0700 Subject: [PATCH 321/607] Action widget fuzzyMatch fix (#191687) fixes fuzzy matching confusion - makes sure only finds exact matching --- src/vs/base/browser/ui/list/listWidget.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index ee900068b4b..b077aa3ff69 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -532,13 +532,16 @@ class TypeNavigationController implements IDisposable { const prefix = matchesPrefix(word, labelStr); const fuzzy = matchesFuzzy2(word, labelStr); - // ensures that when fuzzy matching, it doesn't clash with prefix matching (1 input vs 1+ should be prefix and fuzzy respecitvely) - const fuzzyScore = fuzzy ? fuzzy[0].end - fuzzy[0].start : 0; - if (prefix || fuzzyScore > 1) { - this.previouslyFocused = start; - this.list.setFocus([index]); - this.list.reveal(index); - return; + if (fuzzy) { + const fuzzyScore = fuzzy[0].end - fuzzy[0].start; + + // ensures that when fuzzy matching, doesn't clash with prefix matching (1 input vs 1+ should be prefix and fuzzy respecitvely). Also makes sure that exact matches are prioritized. + if (prefix || (fuzzyScore > 1 && fuzzy.length === 1)) { + this.previouslyFocused = start; + this.list.setFocus([index]); + this.list.reveal(index); + return; + } } } } else { From 5413247e57fb7e3d29cd36f08266005fe72bbde4 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 29 Aug 2023 13:57:48 -0700 Subject: [PATCH 322/607] serve-web: delete socket file on server shutdown (#191692) Fixes #191691 --- cli/src/commands/serve_web.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs index 8d37427dd33..2181c6339a4 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -25,6 +25,7 @@ use crate::download_cache::DownloadCache; use crate::log; use crate::options::Quality; use crate::state::{LauncherPaths, PersistedState}; +use crate::tunnels::shutdown_signal::ShutdownRequest; use crate::update_service::{ unzip_downloaded_release, Platform, Release, TargetKind, UpdateService, }; @@ -98,12 +99,20 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result(service) } }; + let mut shutdown = ShutdownRequest::create_rx([ShutdownRequest::CtrlC]); let r = if let Some(s) = args.socket_path { - let socket = listen_socket_rw_stream(&PathBuf::from(&s)).await?; - ctx.log.result(format!("Web UI available on {}", s)); - Server::builder(socket.into_pollable()) + let s = PathBuf::from(&s); + let socket = listen_socket_rw_stream(&s).await?; + ctx.log + .result(format!("Web UI available on {}", s.display())); + let r = Server::builder(socket.into_pollable()) .serve(make_service_fn(|_| make_svc())) - .await + .with_graceful_shutdown(async { + let _ = shutdown.wait().await; + }) + .await; + let _ = std::fs::remove_file(&s); // cleanup + r } else { let addr: SocketAddr = match &args.host { Some(h) => { @@ -120,6 +129,9 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result Date: Tue, 29 Aug 2023 14:05:13 -0700 Subject: [PATCH 323/607] fix #191591 --- .../accessibility/browser/terminalAccessibilityHelp.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 206e04d47cc..9205be2e624 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -60,7 +60,14 @@ export class TerminalAccessibleContentProvider extends Disposable implements IAc const kb = this._keybindingService.lookupKeybindings(commandId); // Run recent command has multiple keybindings. lookupKeybinding just returns the first one regardless of the when context. // Thus, we have to check if accessibility mode is enabled to determine which keybinding to use. - return this._accessibilityService.isScreenReaderOptimized() ? format(msg, kb[1].getAriaLabel()) : format(msg, kb[0].getAriaLabel()); + const isScreenReaderOptimized = this._accessibilityService.isScreenReaderOptimized(); + if (isScreenReaderOptimized && kb[1]) { + format(msg, kb[1].getAriaLabel()); + } else if (kb[0]) { + format(msg, kb[0].getAriaLabel()); + } else { + return format(noKbMsg, commandId); + } } const kb = this._keybindingService.lookupKeybinding(commandId, this._contextKeyService)?.getAriaLabel(); return !kb ? format(noKbMsg, commandId) : format(msg, kb); From 16c0c5796ed44db224741b6899cd3e0b7c464d1e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Aug 2023 14:24:58 -0700 Subject: [PATCH 324/607] fix #191672 --- .../workbench/contrib/accessibility/browser/accessibleView.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 42e95d10c93..1f82f5c1e9b 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -506,9 +506,9 @@ class AccessibleView extends Disposable { let hint = ''; const disableKeybinding = this._keybindingService.lookupKeybinding(AccessibilityCommandId.DisableVerbosityHint, this._contextKeyService)?.getAriaLabel(); if (disableKeybinding) { - hint = localize('acessibleViewDisableHint', "Disable the aria label hint to open this ({0}).\n", disableKeybinding); + hint = localize('acessibleViewDisableHint', "Disable accessibility verbosity for this feature ({0}). This will disable the hint to open the accessible view for example.\n", disableKeybinding); } else { - hint = localize('accessibleViewDisableHintNoKb', "Add a keybinding for the command Disable Accessible View Hint to disable this hint.\n"); + hint = localize('accessibleViewDisableHintNoKb', "Add a keybinding for the command Disable Accessible View Hint, which disables accessibility verbosity for this feature.\n"); } return hint; } From 3a597af3e6260df6dba8f47025b4c077eed4aa78 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Aug 2023 14:33:50 -0700 Subject: [PATCH 325/607] fix #191684 --- .../accessibility/browser/accessibilityContributions.ts | 4 ++++ .../contrib/accessibility/browser/accessibleViewActions.ts | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts index 720720b02df..90544aabea6 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityContributions.ts @@ -258,6 +258,10 @@ function getActionsFromNotification(notification: INotificationViewItem): IActio }; } } + const manageExtension = actions?.find(a => a.label.includes('Manage Extension')); + if (manageExtension) { + manageExtension.class = ThemeIcon.asClassName(Codicon.gear); + } if (actions) { actions.push({ id: 'clearNotification', label: localize('clearNotification', "Clear Notification"), tooltip: localize('clearNotification', "Clear Notification"), run: () => notification.close(), enabled: true, class: ThemeIcon.asClassName(Codicon.clearAll) }); } diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts index 21547678e51..0721b08a570 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleViewActions.ts @@ -160,7 +160,7 @@ class AccessibleViewDisableHintAction extends Action2 { primary: KeyMod.Alt | KeyCode.F6, weight: KeybindingWeight.WorkbenchContrib }, - icon: Codicon.treeFilterClear, + icon: Codicon.bellSlash, menu: [ commandPalette, { From 5c3a9678d8098374cf09bd184784f43b93817773 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 29 Aug 2023 14:46:55 -0700 Subject: [PATCH 326/607] fix #191722 --- .../accessibility/browser/accessibilityConfiguration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 05812456635..384572c7402 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -42,7 +42,7 @@ export const enum AccessibilityVerbositySettingId { Editor = 'accessibility.verbosity.editor', Hover = 'accessibility.verbosity.hover', Notification = 'accessibility.verbosity.notification', - EditorUntitledHint = 'accessibility.verbosity.editor.untitledHint' + EditorUntitledHint = 'accessibility.verbosity.untitledHint' } export const enum AccessibleViewProviderId { @@ -107,7 +107,7 @@ const configuration: IConfigurationNode = { ...baseProperty }, [AccessibilityVerbositySettingId.EditorUntitledHint]: { - description: localize('verbosity.editor.untitledhint', 'Provide information about relevant actions in an untitled text editor.'), + description: localize('verbosity.untitledhint', 'Provide information about relevant actions in an untitled text editor.'), ...baseProperty } } From 86a82d46a838fc516cc19e3b4a69249232bf74c3 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 29 Aug 2023 15:42:20 -0700 Subject: [PATCH 327/607] Skip Nb Sticky Scroll testing on web platform. (#191716) * test change * skip notebook sticky scroll testing on web --- .../notebook/test/browser/notebookStickyScroll.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts index 708b8b7aea8..99cb8117b61 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { isWeb } from 'vs/base/common/platform'; import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { mock } from 'vs/base/test/common/mock'; @@ -19,7 +20,7 @@ import { createNotebookCellList, setupInstantiationService, withTestNotebook } f import { OutlineTarget } from 'vs/workbench/services/outline/browser/outline'; -suite('NotebookEditorStickyScroll', () => { +(isWeb ? suite.skip : suite)('NotebookEditorStickyScroll', () => { let disposables: DisposableStore; let instantiationService: TestInstantiationService; @@ -92,7 +93,6 @@ suite('NotebookEditorStickyScroll', () => { const notebookOutlineEntries = getOutline(editor).entries; const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries); - await assertSnapshot(resultingMap); }); }); From 4b82860e269125af32a53be167edf7f88c461845 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Tue, 29 Aug 2023 17:45:50 -0700 Subject: [PATCH 328/607] Clicking into notebook markdown search result clears result (#191731) Fixes #191666 --- src/vs/workbench/contrib/search/browser/searchView.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index b87d1347816..dd150169376 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -1894,7 +1894,6 @@ export class SearchView extends ViewPane { pinned, selection, revealIfVisible: true, - indexedCellOptions: element instanceof MatchInNotebook ? { index: element.cellIndex, selection: element.range() } : undefined, }; try { From 20f00913476c551011c0ffc81f03493e8da72eb7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Aug 2023 17:46:46 -0700 Subject: [PATCH 329/607] Don't allow taking action on codeblocks in filtered responses (#191732) Fix microsoft/vscode-copilot#1148 --- .../browser/actions/chatCodeblockActions.ts | 24 +++++++++++++++++-- .../chat/browser/actions/chatTitleActions.ts | 4 ++-- .../contrib/chat/browser/chatListRenderer.ts | 13 ++++++++-- .../contrib/chat/common/chatContextKeys.ts | 1 + 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 3761e137dc5..8439a5d14fd 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -11,8 +11,10 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; +import { RelatedContextItem, WorkspaceEdit } from 'vs/editor/common/languages'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextModel } from 'vs/editor/common/model'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { CopyAction } from 'vs/editor/contrib/clipboard/browser/clipboard'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; @@ -31,8 +33,6 @@ import { CellKind, NOTEBOOK_EDITOR_ID } from 'vs/workbench/contrib/notebook/comm import { ITerminalEditorService, ITerminalGroupService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { WorkspaceEdit, RelatedContextItem } from 'vs/editor/common/languages'; export interface IChatCodeBlockActionContext { code: string; @@ -92,6 +92,11 @@ export function registerChatCodeBlockActions() { return; } + if (context.element.errorDetails?.responseIsFiltered) { + // When run from command palette + return; + } + const clipboardService = accessor.get(IClipboardService); clipboardService.writeText(context.code); @@ -183,6 +188,11 @@ export function registerChatCodeBlockActions() { const editorService = accessor.get(IEditorService); const textFileService = accessor.get(ITextFileService); + if (context.element.errorDetails?.responseIsFiltered) { + // When run from command palette + return; + } + if (editorService.activeEditorPane?.getId() === NOTEBOOK_EDITOR_ID) { return this.handleNotebookEditor(accessor, editorService.activeEditorPane.getControl() as INotebookEditor, context); } @@ -312,6 +322,11 @@ export function registerChatCodeBlockActions() { } override async runWithContext(accessor: ServicesAccessor, context: IChatCodeBlockActionContext) { + if (context.element.errorDetails?.responseIsFiltered) { + // When run from command palette + return; + } + const editorService = accessor.get(IEditorService); const chatService = accessor.get(IChatService); editorService.openEditor({ contents: context.code, languageId: context.languageId, resource: undefined }); @@ -358,6 +373,11 @@ export function registerChatCodeBlockActions() { } override async runWithContext(accessor: ServicesAccessor, context: IChatCodeBlockActionContext) { + if (context.element.errorDetails?.responseIsFiltered) { + // When run from command palette + return; + } + const chatService = accessor.get(IChatService); const terminalService = accessor.get(ITerminalService); const editorService = accessor.get(IEditorService); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index 19bd054e7b8..6d7a399b006 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -15,7 +15,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { ResourceNotebookCellEdit } from 'vs/workbench/contrib/bulkEdit/browser/bulkCellEdits'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; -import { CONTEXT_IN_CHAT_INPUT, CONTEXT_IN_CHAT_SESSION, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; +import { CONTEXT_IN_CHAT_INPUT, CONTEXT_IN_CHAT_SESSION, CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatService, IChatUserActionEvent, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { isRequestVM, isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; @@ -119,7 +119,7 @@ export function registerChatTitleActions() { id: MenuId.ChatMessageTitle, group: 'navigation', isHiddenByDefault: true, - when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, CONTEXT_RESPONSE) + when: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED.negate()) } }); } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 1208e6134c3..06f2c24dfb6 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -61,7 +61,7 @@ import { IChatCodeBlockActionContext } from 'vs/workbench/contrib/chat/browser/a import { ChatTreeItem, IChatCodeBlockInfo, IChatFileTreeInfo } from 'vs/workbench/contrib/chat/browser/chat'; import { ChatFollowups } from 'vs/workbench/contrib/chat/browser/chatFollowups'; import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; -import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_HAS_PROVIDER_ID, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; +import { CONTEXT_REQUEST, CONTEXT_RESPONSE, CONTEXT_RESPONSE_FILTERED, CONTEXT_RESPONSE_HAS_PROVIDER_ID, CONTEXT_RESPONSE_VOTE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatReplyFollowup, IChatResponseProgressFileTreeData, IChatService, ISlashCommand, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatResponseMarkdownRenderData, IChatResponseRenderData, IChatResponseViewModel, IChatWelcomeMessageViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IWordCountResult, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; @@ -268,10 +268,13 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer('chatSessionResponseHasProviderId', false, { type: 'boolean', description: localize('interactiveSessionResponseHasProviderId', "True when the provider has assigned an id to this response.") }); export const CONTEXT_RESPONSE_VOTE = new RawContextKey('chatSessionResponseVote', '', { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") }); +export const CONTEXT_RESPONSE_FILTERED = new RawContextKey('chatSessionResponseFiltered', false, { type: 'boolean', description: localize('chatResponseFiltered', "True when the chat response was filtered out by the server.") }); export const CONTEXT_CHAT_REQUEST_IN_PROGRESS = new RawContextKey('chatSessionRequestInProgress', false, { type: 'boolean', description: localize('interactiveSessionRequestInProgress', "True when the current request is still in progress.") }); export const CONTEXT_RESPONSE = new RawContextKey('chatResponse', false, { type: 'boolean', description: localize('chatResponse', "The chat item is a response.") }); From 046cfbf6d0b50a0cbd74d722451870bdcdacd30c Mon Sep 17 00:00:00 2001 From: Hans Date: Wed, 30 Aug 2023 09:04:15 +0800 Subject: [PATCH 330/607] add custom hover for quick open (#191416) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add custom hover for quick open * 💄 * 💄 * adjust delayer create logic --- .../platform/quickinput/browser/quickInput.ts | 2 +- .../browser/quickInputController.ts | 5 +- .../quickinput/browser/quickInputList.ts | 88 ++++++++++--------- .../quickinput/browser/quickInputService.ts | 6 -- 4 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 173bbc88096..4258cd0640a 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -47,7 +47,7 @@ export interface IQuickInputOptions { renderers: IListRenderer[], options: IListOptions, ): List; - hoverDelegate: IHoverDelegate; + hoverDelegate?: IHoverDelegate; styles: IQuickInputStyles; } diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index 85780828ce1..932eec180e1 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -83,12 +83,13 @@ export class QuickInputController extends Disposable { const titleBar = dom.append(container, $('.quick-input-titlebar')); - const leftActionBar = this._register(new ActionBar(titleBar)); + const actionBarOption = this.options.hoverDelegate ? { hoverDelegate: this.options.hoverDelegate } : undefined; + const leftActionBar = this._register(new ActionBar(titleBar, actionBarOption)); leftActionBar.domNode.classList.add('quick-input-left-action-bar'); const title = dom.append(titleBar, $('.quick-input-title')); - const rightActionBar = this._register(new ActionBar(titleBar)); + const rightActionBar = this._register(new ActionBar(titleBar, actionBarOption)); rightActionBar.domNode.classList.add('quick-input-right-action-bar'); const headerContainer = dom.append(container, $('.quick-input-header')); diff --git a/src/vs/platform/quickinput/browser/quickInputList.ts b/src/vs/platform/quickinput/browser/quickInputList.ts index e5914bfef84..bb8dc4c1429 100644 --- a/src/vs/platform/quickinput/browser/quickInputList.ts +++ b/src/vs/platform/quickinput/browser/quickInputList.ts @@ -541,46 +541,49 @@ export class QuickInputList { } })); - const delayer = new ThrottledDelayer(options.hoverDelegate.delay); - // onMouseOver triggers every time a new element has been moused over - // even if it's on the same list item. - this.disposables.push(this.list.onMouseOver(async e => { - // If we hover over an anchor element, we don't want to show the hover because - // the anchor may have a tooltip that we want to show instead. - if (e.browserEvent.target instanceof HTMLAnchorElement) { - delayer.cancel(); - return; - } - if ( - // anchors are an exception as called out above so we skip them here - !(e.browserEvent.relatedTarget instanceof HTMLAnchorElement) && - // check if the mouse is still over the same element - dom.isAncestor(e.browserEvent.relatedTarget as Node, e.element?.element as Node) - ) { - return; - } - try { - await delayer.trigger(async () => { - if (e.element) { - this.showHover(e.element); - } - }); - } catch (e) { - // Ignore cancellation errors due to mouse out - if (!isCancellationError(e)) { - throw e; + if (options.hoverDelegate) { + const delayer = new ThrottledDelayer(options.hoverDelegate.delay); + // onMouseOver triggers every time a new element has been moused over + // even if it's on the same list item. + this.disposables.push(this.list.onMouseOver(async e => { + // If we hover over an anchor element, we don't want to show the hover because + // the anchor may have a tooltip that we want to show instead. + if (e.browserEvent.target instanceof HTMLAnchorElement) { + delayer.cancel(); + return; } - } - })); - this.disposables.push(this.list.onMouseOut(e => { - // onMouseOut triggers every time a new element has been moused over - // even if it's on the same list item. We only want one event, so we - // check if the mouse is still over the same element. - if (dom.isAncestor(e.browserEvent.relatedTarget as Node, e.element?.element as Node)) { - return; - } - delayer.cancel(); - })); + if ( + // anchors are an exception as called out above so we skip them here + !(e.browserEvent.relatedTarget instanceof HTMLAnchorElement) && + // check if the mouse is still over the same element + dom.isAncestor(e.browserEvent.relatedTarget as Node, e.element?.element as Node) + ) { + return; + } + try { + await delayer.trigger(async () => { + if (e.element) { + this.showHover(e.element); + } + }); + } catch (e) { + // Ignore cancellation errors due to mouse out + if (!isCancellationError(e)) { + throw e; + } + } + })); + this.disposables.push(this.list.onMouseOut(e => { + // onMouseOut triggers every time a new element has been moused over + // even if it's on the same list item. We only want one event, so we + // check if the mouse is still over the same element. + if (dom.isAncestor(e.browserEvent.relatedTarget as Node, e.element?.element as Node)) { + return; + } + delayer.cancel(); + })); + this.disposables.push(delayer); + } this.disposables.push(this._listElementChecked.event(_ => this.fireCheckedEvents())); this.disposables.push( this._onChangedAllVisibleChecked, @@ -590,8 +593,7 @@ export class QuickInputList { this._onButtonTriggered, this._onSeparatorButtonTriggered, this._onLeave, - this._onKeyDown, - delayer + this._onKeyDown ); } @@ -839,10 +841,14 @@ export class QuickInputList { * @param element The element to show the hover for */ private showHover(element: IListElement): void { + if (this.options.hoverDelegate === undefined) { + return; + } if (this._lastHover && !this._lastHover.isDisposed) { this.options.hoverDelegate.onDidHideHover?.(); this._lastHover?.dispose(); } + if (!element.element || !element.saneTooltip) { return; } diff --git a/src/vs/platform/quickinput/browser/quickInputService.ts b/src/vs/platform/quickinput/browser/quickInputService.ts index c4d20832264..924e6b83ef6 100644 --- a/src/vs/platform/quickinput/browser/quickInputService.ts +++ b/src/vs/platform/quickinput/browser/quickInputService.ts @@ -86,12 +86,6 @@ export class QuickInputService extends Themable implements IQuickInputService { renderers: IListRenderer[], options: IWorkbenchListOptions ) => this.instantiationService.createInstance(WorkbenchList, user, container, delegate, renderers, options) as List, - hoverDelegate: { - showHover(options, focus) { - return undefined; - }, - delay: 200 - }, styles: this.computeStyles() }; From ed40013ae9fc09ae0ed47fbc46115701c3323813 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 29 Aug 2023 20:28:51 -0700 Subject: [PATCH 331/607] Only decorate complete @ variables (#191733) --- .../browser/contrib/chatInputEditorContrib.ts | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 8be40c833c1..d4ff01aec83 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; +import { Iterable } from 'vs/base/common/iterator'; import { Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Position } from 'vs/editor/common/core/position'; @@ -44,6 +45,7 @@ class InputEditorDecorations extends Disposable { @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IThemeService private readonly themeService: IThemeService, @IChatService private readonly chatService: IChatService, + @IChatVariablesService private readonly chatVariablesService: IChatVariablesService, ) { super(); @@ -175,21 +177,22 @@ class InputEditorDecorations extends Disposable { this.widget.inputEditor.setDecorationsByType(decorationDescription, slashCommandTextDecorationType, []); } - // const variables = this.chatVariablesService.getVariables(); + const variables = this.chatVariablesService.getVariables(); const variableReg = /(^|\s)@(\w+)(:\d+)?(?=(\s|$))/ig; let match: RegExpMatchArray | null; const varDecorations: IDecorationOptions[] = []; while (match = variableReg.exec(inputValue)) { - // const candidate = match[2]; - // if (Iterable.find(variables, v => v.name === candidate)) - varDecorations.push({ - range: { - startLineNumber: 1, - endLineNumber: 1, - startColumn: match.index! + match[1].length + 1, - endColumn: match.index! + match[0].length + 1 - } - }); + const varName = match[2]; + if (Iterable.find(variables, v => v.name === varName)) { + varDecorations.push({ + range: { + startLineNumber: 1, + endLineNumber: 1, + startColumn: match.index! + match[1].length + 1, + endColumn: match.index! + match[0].length + 1 + } + }); + } } this.widget.inputEditor.setDecorationsByType(decorationDescription, variableTextDecorationType, varDecorations); From 35be9bf683eace09796e59d54f1f225bbc3a7866 Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 30 Aug 2023 13:03:40 +0900 Subject: [PATCH 332/607] chore: update electron@25.7.0 (#191282) * chore: update electron@25.7.0 * chore: update internal build id * chore: bump distro --- .yarnrc | 4 +- build/checksums/electron.txt | 54 +++++++++---------- cgmanifest.json | 4 +- package.json | 4 +- .../api/node/extensionHostProcess.ts | 10 ---- yarn.lock | 8 +-- 6 files changed, 37 insertions(+), 47 deletions(-) diff --git a/.yarnrc b/.yarnrc index 8f658afd4a9..7b3fff4b526 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "25.5.0" -ms_build_id "23084831" +target "25.7.0" +ms_build_id "23434598" runtime "electron" build_from_source "true" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index f5c22bc1c23..a19497f08e8 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,27 +1,27 @@ -c3fb8cb4804143eb25fce55a179a6f2df8c215ed709104ec235c96e357b20f42 *electron-v25.5.0-darwin-arm64-symbols.zip -e6d2a09348d4fe7c9fbd92bf796489a95e625642f0f1ce96169212554cfa6841 *electron-v25.5.0-darwin-arm64.zip -15c28e613dfee0f7e46a296bb4aed64e17f9644d7ef19129aeb6480b8da230ab *electron-v25.5.0-darwin-x64-symbols.zip -a5c5c0b621daf8242258c89edb2387fbdf1c69125c984f8564d89b87927373e3 *electron-v25.5.0-darwin-x64.zip -69d60a69b7f692b9069cdf9e518bcbaca9cec561f40199cc87196929936a34ce *electron-v25.5.0-linux-arm64-symbols.zip -adec2ba09faf5f8d8af8997c8bed43c7712eba5546db1e7d06f8357bf4613921 *electron-v25.5.0-linux-arm64.zip -0fe7a5d152c9c401671e02ebcd9da34ab9bb0c28598ede3657077a79f8b3e70a *electron-v25.5.0-linux-armv7l-symbols.zip -eccb66e4a308a0bd2d90474894370e3d687e32f78672e6b5077c1822c7bc526b *electron-v25.5.0-linux-armv7l.zip -82bd9bc9e66f8ae802fe48a51b8d7e2fb599403e9715fa4b859190200a7376b1 *electron-v25.5.0-linux-x64-symbols.zip -485cbeb206fccfb4ed42f694100eebb80c9db6639b3537a95823c4fcb7f210cd *electron-v25.5.0-linux-x64.zip -ed2c2a7da571b53bcb336b9a2a024753a272df82ece45df83df888df51bf1912 *electron-v25.5.0-win32-arm64-pdb.zip -c316f6364e9b4cd61e19d4763c96abda7247a2c31ae7d30e81219c9a7754f11a *electron-v25.5.0-win32-arm64-symbols.zip -582ccbfc5a85a093f5639ee476bb5fef18c2d25dab4a60e5f5cb47e64c99f7fb *electron-v25.5.0-win32-arm64.zip -5776e650c23e3847b0c52d850f61c57a84a4fa30943c3cc82197250112911b5b *electron-v25.5.0-win32-ia32-pdb.zip -730b429ad2c4cae0fe3ba9600a6ee86c79dada77976bac1c75ca2404993495bc *electron-v25.5.0-win32-ia32-symbols.zip -7e6e68aa33a89c0d647575b06daf415a2401feb170ebb9cd795e221af321f751 *electron-v25.5.0-win32-ia32.zip -0792665fda9255b340a829cdd24601887a2ca8f04cc49f9a6d4557db0c0cd2f2 *electron-v25.5.0-win32-x64-pdb.zip -dc2459546951f8418e866857e9111dd83a0789d401906b2200c2f8dd59d9146b *electron-v25.5.0-win32-x64-symbols.zip -9bf7980fbc024ba77ea8ab3e2d32088a5f69bf32506a7d2db72ede17028abdf4 *electron-v25.5.0-win32-x64.zip -5b1ea601b737842eacf88d7456c4d14e697822753e9a08ede889cce9e20bafb2 *ffmpeg-v25.5.0-darwin-arm64.zip -37bf5c75edefc0b6735b44d0b5b06fdd427179a8501f6e93df9840283cdb4a95 *ffmpeg-v25.5.0-darwin-x64.zip -bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.5.0-linux-arm64.zip -9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.5.0-linux-armv7l.zip -edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.5.0-linux-x64.zip -2e28767b3570ea247869a20988cddd23af710eb994d6099404f123390cedeba6 *ffmpeg-v25.5.0-win32-arm64.zip -715568eefd7267573a30186ade3de901587baeb1f013200d8ae50b35941b613f *ffmpeg-v25.5.0-win32-ia32.zip -29876504452aaa505f696642178968e24b8dc8cc4b055071e6f0f3f073088acc *ffmpeg-v25.5.0-win32-x64.zip +efbcf77eb1a0783766f9579ffb9f9b68f04fea8cb091eab7ab8484ba0cd13fbf *electron-v25.7.0-darwin-arm64-symbols.zip +76a415165d212a345a5689de83078adc715fc10562bfaa35d7323094780ba683 *electron-v25.7.0-darwin-arm64.zip +07b9049848e877019d1dce71e06713125b605dda8ac5d0b8ab3aa899cf40551d *electron-v25.7.0-darwin-x64-symbols.zip +dea726ae9adc1c36206ce8d20ce32f630bcd684b869e0cb302f97c8bd26616d6 *electron-v25.7.0-darwin-x64.zip +b6c8ba123353984b2d3ffd6ccd52aec2d3238f71611c4c94bab75aa92804eebf *electron-v25.7.0-linux-arm64-symbols.zip +19e1e2c7ea1ab024f069e3dad6a26605e14b2c605e134484196343118fccf925 *electron-v25.7.0-linux-arm64.zip +ba0bbe84ea626c8064809c66487a3b77ad39bcf8b1daa0d9421428f78ad4d665 *electron-v25.7.0-linux-armv7l-symbols.zip +832a68cddb20eb847aca982b89f89e145f50dd483c71c8a705bbb9248fb7c665 *electron-v25.7.0-linux-armv7l.zip +2e616b446112533d3aa69ed1074ab1e0be5400996129aa636273d01462dc9506 *electron-v25.7.0-linux-x64-symbols.zip +002641e8103b77060e23b9c77c51ffb942372d01306210cdc3d32fc6ae5d112b *electron-v25.7.0-linux-x64.zip +162e0f7ca9fc1c17b8d84e9b9eccc65bb0f527a67f6339a19292d798085848e4 *electron-v25.7.0-win32-arm64-pdb.zip +7d98734ffcf10e1d002c30a212dd1f203b1418a295da67410490f83e9ced388c *electron-v25.7.0-win32-arm64-symbols.zip +9777d47f74d129f7c68ebffad640a6a527b83895c173c7d344f80fc9588bad85 *electron-v25.7.0-win32-arm64.zip +c805c6356378dccb21b5725004934534e187bdaf8149a6a457fdd60d243b41e4 *electron-v25.7.0-win32-ia32-pdb.zip +5f1a3b09153cf934f24f3b1853ad1788e7c27c6ddceb80e52fe07e2e69b6bb2b *electron-v25.7.0-win32-ia32-symbols.zip +fdf8e100c3d3cdb75b54ced1ecae96d6206eca08ebb07c5d8f08740e5e703509 *electron-v25.7.0-win32-ia32.zip +aa56314a675351e9457355f2cb0660c62a3be62cc340dad76fd216741064824d *electron-v25.7.0-win32-x64-pdb.zip +25d664dfe0823e1a12269feb6eb3886dba44b2d130b8787c4d58d3d0cbcf1c22 *electron-v25.7.0-win32-x64-symbols.zip +7ddb0b38207fd837cdf4e2b2778c365751315e321b09d346c8bb8476300d0ec0 *electron-v25.7.0-win32-x64.zip +02619733aadb13b6bf21df966e04775506d0d7595a0795003fed45631c4a0af6 *ffmpeg-v25.7.0-darwin-arm64.zip +69a8e2021e48f504021913c15633cbef2b4a7b28656c51cd238acdbf7c94e358 *ffmpeg-v25.7.0-darwin-x64.zip +bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.7.0-linux-arm64.zip +9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.7.0-linux-armv7l.zip +edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.7.0-linux-x64.zip +7076d4593f2e2e2abf0dc9ad8f6490d72b2fa89710def822f39da4363e49e504 *ffmpeg-v25.7.0-win32-arm64.zip +bd07183c1b6a93586d73c4106ceef0faae77f46763d15d6901d5954c2c5bba1b *ffmpeg-v25.7.0-win32-ia32.zip +b056e71a7c59441c551d5bbc1a8d99f2464a5809a3ba17d41540dc7174cab7b7 *ffmpeg-v25.7.0-win32-x64.zip diff --git a/cgmanifest.json b/cgmanifest.json index 574ec75d24d..df2f75f3209 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "34be316c404e84cdd967fa0e10fceeb6424eed90" + "commitHash": "f818ec3295c9688585e3cfea532ccc5b705746bb" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "25.5.0" + "version": "25.7.0" }, { "component": { diff --git a/package.json b/package.json index 276a62f924f..0af191524ed 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "2100ad274ed566f978bb917f327b3d99f95d59f2", + "distro": "c7d53f94cfb25168c3c3ae9a6f4902d32643a0ee", "author": { "name": "Microsoft Corporation" }, @@ -150,7 +150,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "25.5.0", + "electron": "25.7.0", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 860800e3f84..bb3cbfbca7b 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -6,7 +6,6 @@ import * as nativeWatchdog from 'native-watchdog'; import * as net from 'net'; import * as minimist from 'minimist'; -import * as dns from 'dns'; import * as performance from 'vs/base/common/performance'; import type { MessagePortMain } from 'vs/base/parts/sandbox/node/electronTypes'; import { isCancellationError, isSigPipeError, onUnexpectedError } from 'vs/base/common/errors'; @@ -47,15 +46,6 @@ interface ParsedExtHostArgs { } })(); -// TODO(deepak1556): Remove this once -// https://github.com/electron/electron/pull/39376 -// is available. The following API call is needed to get our -// remote integration tests to pass. -(function configureDnsResultOrder() { - // Refs https://github.com/microsoft/vscode/issues/189805 - dns.setDefaultResultOrder('ipv4first'); -})(); - const args = minimist(process.argv.slice(2), { boolean: [ 'transformURIs', diff --git a/yarn.lock b/yarn.lock index 582d6e845a1..e171d0e0f55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3587,10 +3587,10 @@ electron-to-chromium@^1.4.202: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.207.tgz#9c3310ebace2952903d05dcaba8abe3a4ed44c01" integrity sha512-piH7MJDJp4rJCduWbVvmUd59AUne1AFBJ8JaRQvk0KzNTSUnZrVXHCZc+eg+CGE4OujkcLJznhGKD6tuAshj5Q== -electron@25.5.0: - version "25.5.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-25.5.0.tgz#6465d49c0731424e3e48776628c35771697caf11" - integrity sha512-w1DNj1LuAk0Vaas1rQ0pAkTe2gZ5YG75J27mC2m88y0G6Do5b5YoFDaF84fOGQHeQ4j8tC5LngSgWhbwmqDlrw== +electron@25.7.0: + version "25.7.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-25.7.0.tgz#0076c2e6acfe363f666a7b77d826a6f8a3028bcd" + integrity sha512-P82EzYZ8k9J21x5syhXV7EkezDmEXwycReXnagfzS0kwepnrlWzq1aDIUWdNvzTdHobky4m/nYcL98qd73mEVA== dependencies: "@electron/get" "^2.0.0" "@types/node" "^18.11.18" From 82348c380c484b77a3ad03566534a4735fa391c6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 30 Aug 2023 11:19:11 +0200 Subject: [PATCH 333/607] fix #191734 (#191756) --- .../node/extensionManagementService.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 677149f937b..9b8ba9a1b42 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -152,12 +152,13 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi throw new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with VS Code '{1}'.", extensionId, this.productService.version)); } - const result = await this.installExtensions([{ manifest, extension: location, options }]); - if (result[0]?.local) { - return result[0]?.local; + const results = await this.installExtensions([{ manifest, extension: location, options }]); + const result = results.find(({ identifier }) => areSameExtensions(identifier, { id: extensionId })); + if (result?.local) { + return result.local; } - if (result[0]?.error) { - throw result[0].error; + if (result?.error) { + throw result.error; } throw toExtensionManagementError(new Error(`Unknown error while installing extension ${extensionId}`)); } finally { From ebfe7fabfb11c763896c20c09ea6b386fbf021e3 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 30 Aug 2023 11:37:44 +0200 Subject: [PATCH 334/607] wip - define jsdoc lint rules for vscode.d.ts --- .eslintrc.json | 41 +++++++++++++++++++++++++++ package.json | 2 +- yarn.lock | 76 +++++++++++++++++++++++++++++--------------------- 3 files changed, 86 insertions(+), 33 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 0644079623d..b2c303da84a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -196,6 +196,47 @@ ] } }, + { + "files": [ + "**/vscode.d.ts" + ], + "rules": { + "extends": [ + "plugin:jsdoc/recommended-typescript" + ], + "jsdoc/tag-lines": "off", + "jsdoc/valid-types": "off", + "jsdoc/no-multi-asterisks": [ + "warn", + { + "allowWhitespace": true + } + ], + "jsdoc/require-jsdoc": [ + "warn", + { + "enableFixer": false, + "contexts": [ + "TSInterfaceDeclaration", + "TSPropertySignature", + "TSMethodSignature", + "ClassDeclaration", + "MethodDefinition", + "PropertyDeclaration", + "TSEnumDeclaration", + "TSEnumMember", + "ExportNamedDeclaration" + ] + } + ], + "jsdoc/check-param-names": [ + "warn", + { + "enableFixer": false + } + ] + } + }, { "files": [ "src/**/{common,browser}/**/*.ts" diff --git a/package.json b/package.json index 4dbaa275413..99b6034460c 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "electron": "25.5.0", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", - "eslint-plugin-jsdoc": "^39.3.2", + "eslint-plugin-jsdoc": "^46.5.0", "eslint-plugin-local": "^1.0.0", "event-stream": "3.3.4", "fancy-log": "^1.3.3", diff --git a/yarn.lock b/yarn.lock index 582d6e845a1..e651e4a891d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -325,14 +325,14 @@ optionalDependencies: global-agent "^3.0.0" -"@es-joy/jsdoccomment@~0.31.0": - version "0.31.0" - resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.31.0.tgz#dbc342cc38eb6878c12727985e693eaef34302bc" - integrity sha512-tc1/iuQcnaiSIUVad72PBierDFpsxdUHtEF/OrfqvM1CBAsIoMP51j52jTMb3dXriwhieTo289InzZj72jL3EQ== +"@es-joy/jsdoccomment@~0.40.1": + version "0.40.1" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz#13acd77fb372ed1c83b7355edd865a3b370c9ec4" + integrity sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg== dependencies: - comment-parser "1.3.1" - esquery "^1.4.0" - jsdoc-type-pratt-parser "~3.1.0" + comment-parser "1.4.0" + esquery "^1.5.0" + jsdoc-type-pratt-parser "~4.0.0" "@eslint-community/eslint-utils@^4.2.0": version "4.4.0" @@ -1841,6 +1841,11 @@ archy@^1.0.0: resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= +are-docs-informative@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" + integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== + are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -2289,6 +2294,11 @@ buffer@^5.2.1, buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + bytes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" @@ -2797,10 +2807,10 @@ commandpost@^1.0.0: resolved "https://registry.yarnpkg.com/commandpost/-/commandpost-1.2.1.tgz#2e9c4c7508b9dc704afefaa91cab92ee6054cc68" integrity sha512-V1wzc+DTFsO96te2W/U+fKNRSOWtOwXhkkZH2WRLLbucrY+YrDNsRr4vtfSf83MUZVF3E6B4nwT30fqaTpzipQ== -comment-parser@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b" - integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA== +comment-parser@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.0.tgz#0f8c560f59698193854f12884c20c0e39a26d32c" + integrity sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw== component-emitter@^1.2.1: version "1.3.0" @@ -3822,17 +3832,19 @@ eslint-plugin-header@3.1.1: resolved "https://registry.yarnpkg.com/eslint-plugin-header/-/eslint-plugin-header-3.1.1.tgz#6ce512432d57675265fac47292b50d1eff11acd6" integrity sha512-9vlKxuJ4qf793CmeeSrZUvVClw6amtpghq3CuWcB5cUNnWHQhgcqy5eF8oVKFk1G3Y/CbchGfEaw3wiIJaNmVg== -eslint-plugin-jsdoc@^39.3.2: - version "39.3.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-39.3.2.tgz#b9c3becdbd860a75b8bd07bd04a0eaaad7c79403" - integrity sha512-RSGN94RYzIJS/WfW3l6cXzRLfJWxvJgNQZ4w0WCaxJWDJMigtwTsILEAfKqmmPkT2rwMH/s3C7G5ChDE6cwPJg== +eslint-plugin-jsdoc@^46.5.0: + version "46.5.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.5.0.tgz#02e7945701a01fab76e7ced850d4d1eea63c23c0" + integrity sha512-aulXdA4I1dyWpzyS1Nh/GNoS6PavzeucxEapnMR4JUERowWvaEk2Y4A5irpHAcdXtBBHLVe8WIhdXNjoAlGQgA== dependencies: - "@es-joy/jsdoccomment" "~0.31.0" - comment-parser "1.3.1" + "@es-joy/jsdoccomment" "~0.40.1" + are-docs-informative "^0.0.2" + comment-parser "1.4.0" debug "^4.3.4" escape-string-regexp "^4.0.0" - esquery "^1.4.0" - semver "^7.3.7" + esquery "^1.5.0" + is-builtin-module "^3.2.1" + semver "^7.5.4" spdx-expression-parse "^3.0.1" eslint-plugin-local@^1.0.0: @@ -3999,14 +4011,7 @@ esquery@^1.0.1: dependencies: estraverse "^5.1.0" -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esquery@^1.4.2: +esquery@^1.4.2, esquery@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== @@ -5683,6 +5688,13 @@ is-buffer@^1.1.5, is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== +is-builtin-module@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + is-callable@^1.1.4, is-callable@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" @@ -6143,10 +6155,10 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== -jsdoc-type-pratt-parser@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-3.1.0.tgz#a4a56bdc6e82e5865ffd9febc5b1a227ff28e67e" - integrity sha512-MgtD0ZiCDk9B+eI73BextfRrVQl0oyzRG8B2BjORts6jbunj4ScKPcyXGTbB6eXL4y9TzxCm6hyeLq/2ASzNdw== +jsdoc-type-pratt-parser@~4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz#136f0571a99c184d84ec84662c45c29ceff71114" + integrity sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ== jsesc@^2.5.1: version "2.5.2" @@ -8895,7 +8907,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== From 4a0169c45c6614c59ffa81daf2c9bf963a007957 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 30 Aug 2023 11:50:58 +0200 Subject: [PATCH 335/607] fix #191022 (#191760) --- .../common/extensionManagementChannelClient.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementChannelClient.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementChannelClient.ts index 305332829c9..c0815fcd50f 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementChannelClient.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementChannelClient.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILocalExtension, IGalleryExtension, InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ILocalExtension, IGalleryExtension, InstallOptions, InstallVSIXOptions, UninstallOptions, Metadata, DidUninstallExtensionEvent, InstallExtensionEvent, InstallExtensionResult, UninstallExtensionEvent, InstallExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; import { ExtensionIdentifier, ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionManagementChannelClient as BaseExtensionManagementChannelClient, ExtensionEventResult } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; @@ -76,6 +76,14 @@ export abstract class ProfileAwareExtensionManagementChannelClient extends BaseE return super.installFromGallery(extension, installOptions); } + override async installGalleryExtensions(extensions: InstallExtensionInfo[]): Promise { + const infos: InstallExtensionInfo[] = []; + for (const extension of extensions) { + infos.push({ ...extension, options: { ...extension.options, profileLocation: extension.options?.profileLocation ? (await this.getProfileLocation(extension.options?.profileLocation)) : undefined } }); + } + return super.installGalleryExtensions(infos); + } + override async uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { options = { ...options, profileLocation: await this.getProfileLocation(options?.profileLocation) }; return super.uninstall(extension, options); From 32d0dbf4d08633d305bd0a893eeebd9100a69cd5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 11:42:58 +0200 Subject: [PATCH 336/607] Fixes #191664 --- .../browser/widget/diffEditorWidget2/unchangedRanges.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 5b5c4ecc2a9..73d56045a0e 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -94,7 +94,7 @@ export class UnchangedRangesFeature extends Disposable { const d = derived(reader => /** @description hiddenOriginalRangeStart */ r.getHiddenOriginalRange(reader).startLineNumber - 1); const origVz = new PlaceholderViewZone(d, 24); origViewZones.push(origVz); - store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource, l => this._diffModel.get()!.ensureOriginalLineIsVisible(l, undefined), this._options)); + store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource, l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined), this._options)); } { const d = derived(reader => /** @description hiddenModifiedRangeStart */ r.getHiddenModifiedRange(reader).startLineNumber - 1); @@ -265,7 +265,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { private readonly _unchangedRegionRange: LineRange, private readonly hide: boolean, private readonly _modifiedOutlineSource: OutlineSource, - private readonly _revealHiddenLine: (lineNumber: number) => void, + private readonly _revealModifiedHiddenLine: (lineNumber: number) => void, private readonly _options: DiffEditorOptions, ) { const root = h('div.diff-hidden-lines-widget'); @@ -396,7 +396,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { ]).root; children.push(divItem); divItem.onclick = () => { - this._revealHiddenLine(item.startLineNumber); + this._revealModifiedHiddenLine(item.startLineNumber); }; } } From e77c84f0a744563a1753f6243898eafcbf3301f3 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 30 Aug 2023 12:07:53 +0200 Subject: [PATCH 337/607] Configure Tunnel Name leads to empty settings page for WSL (#191761) --- .../electron-sandbox/remoteTunnel.contribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts index c71388d51fb..145c7c81a1e 100644 --- a/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts +++ b/src/vs/workbench/contrib/remoteTunnel/electron-sandbox/remoteTunnel.contribution.ts @@ -789,7 +789,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis [CONFIGURATION_KEY_HOST_NAME]: { description: localize('remoteTunnelAccess.machineName', "The name under which the remote tunnel access is registered. If not set, the host name is used."), type: 'string', - scope: ConfigurationScope.MACHINE, + scope: ConfigurationScope.APPLICATION, pattern: '^(\\w[\\w-]*)?$', patternErrorMessage: localize('remoteTunnelAccess.machineNameRegex', "The name must only consist of letters, numbers, underscore and dash. It must not start with a dash."), maxLength: 20, @@ -798,7 +798,7 @@ Registry.as(ConfigurationExtensions.Configuration).regis [CONFIGURATION_KEY_PREVENT_SLEEP]: { description: localize('remoteTunnelAccess.preventSleep', "Prevent the computer from sleeping when remote tunnel access is turned on."), type: 'boolean', - scope: ConfigurationScope.MACHINE, + scope: ConfigurationScope.APPLICATION, default: false, } } From 662ce156c09b569a24c1de6141c5ef2b9c66dc4f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 12:03:55 +0200 Subject: [PATCH 338/607] Fixes #191637 --- .../editor/browser/widget/diffEditorWidget2/unchangedRanges.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index 73d56045a0e..bd1af6d94f9 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -250,7 +250,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { private readonly _nodes = h('div.diff-hidden-lines', [ h('div.top@top', { title: localize('diff.hiddenLines.top', 'Click or drag to show more above') }), h('div.center@content', { style: { display: 'flex' } }, [ - h('div@first', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } }, + h('div@first', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', flexShrink: '0' } }, [$('a', { title: localize('showAll', 'Show all'), role: 'button', onclick: () => { this.showAll(); } }, ...renderLabelWithIcons('$(unfold)'))] ), h('div@others', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } }), From c39b2fffa65713e69c56f77a64bd0bc76483b6d6 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 12:05:18 +0200 Subject: [PATCH 339/607] Fixes #191600 --- .../editor/browser/widget/diffEditorWidget2/unchangedRanges.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts index bd1af6d94f9..a61dc81803b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts @@ -365,7 +365,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { const children: HTMLElement[] = []; if (!this.hide) { const lineCount = _unchangedRegion.getHiddenModifiedRange(reader).length; - const linesHiddenText = localize('hiddenLines', '{0} Hidden Lines', lineCount); + const linesHiddenText = localize('hiddenLines', '{0} hidden lines', lineCount); const span = $('span', { title: localize('diff.hiddenLines.expandAll', 'Double click to unfold') }, linesHiddenText); span.addEventListener('dblclick', e => { if (e.button !== 0) { return; } From 19c238294cb6793aa4d34c8fcdd7746e237cb101 Mon Sep 17 00:00:00 2001 From: troy351 <914053923@qq.com> Date: Wed, 30 Aug 2023 18:24:37 +0800 Subject: [PATCH 340/607] listWidget: remove redundant logic (#191054) --- src/vs/base/browser/ui/list/listWidget.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index b077aa3ff69..262d3ca9e2e 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -732,10 +732,6 @@ export class MouseController implements IDisposable { return; } - if (this.isSelectionRangeChangeEvent(e)) { - return this.changeSelection(e); - } - if (this.isSelectionChangeEvent(e)) { return this.changeSelection(e); } From 4bd1ea339fa9c1e0d5c6a8f39c5b351ae167a4c5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 12:19:07 +0200 Subject: [PATCH 341/607] Fixes #191603 --- .../editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 27a9f8f46fb..8a3ac863ddc 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -324,7 +324,7 @@ class MovedBlockOverlayWidget extends ViewZoneOverlayWidget { true, () => { this._editor.focus(); - this._diffModel.movedTextToCompare.set(this._diffModel.movedTextToCompare.get() ? undefined : this._move, undefined); + this._diffModel.movedTextToCompare.set(this._diffModel.movedTextToCompare.get() === _move ? undefined : this._move, undefined); }, ); this._register(autorun(reader => { From adf839e3564d0a3522249c4f94df518b8ed49067 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 30 Aug 2023 12:42:51 +0200 Subject: [PATCH 342/607] changing the setting text --- src/vs/editor/common/config/editorOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index f338f7b0817..35979acc29f 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2799,7 +2799,7 @@ class EditorStickyScroll extends BaseEditorOption Date: Wed, 30 Aug 2023 14:42:02 +0200 Subject: [PATCH 343/607] voice - fix issues around stopping transcription (#191774) --- src/vs/base/common/event.ts | 18 ++ src/vs/base/test/common/event.test.ts | 44 ++- .../actions/voiceChatActions.ts | 257 +++++++++++------- .../browser/inlineChatController.ts | 1 + 4 files changed, 218 insertions(+), 102 deletions(-) diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 52a4b2d20f6..c26c0271f39 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -557,6 +557,24 @@ export namespace Event { return new Promise(resolve => once(event)(resolve)); } + /** + * Creates an event out of a promise that fires once when the promise is + * resolved with the result of the promise or `undefined`. + */ + export function fromPromise(promise: Promise): Event { + const result = new Emitter(); + + promise.then(res => { + result.fire(res); + }, () => { + result.fire(undefined); + }).finally(() => { + result.dispose(); + }); + + return result.event; + } + /** * Adds a listener to an event and calls the listener immediately with undefined as the event object. * diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 8f14d7d47cd..30320370f2a 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import { stub } from 'sinon'; -import { timeout } from 'vs/base/common/async'; +import { DeferredPromise, timeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { errorHandler, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { AsyncEmitter, DebounceEmitter, DynamicListEventMultiplexer, Emitter, Event, EventBufferer, EventMultiplexer, IWaitUntil, MicrotaskEmitter, PauseableEmitter, Relay, createEventDeliveryQueue } from 'vs/base/common/event'; @@ -1158,6 +1158,48 @@ suite('Event utils', () => { listener.dispose(); // should not crash }); + suite('fromPromise', () => { + + test('not yet resolved', async function () { + return new Promise(resolve => { + let promise = new DeferredPromise(); + + Event.fromPromise(promise.p)(e => { + assert.strictEqual(e, 1); + + promise = new DeferredPromise(); + + Event.fromPromise(promise.p)(() => { + resolve(); + }); + + promise.error(undefined); + }); + + promise.complete(1); + }); + }); + + test('already resolved', async function () { + return new Promise(resolve => { + let promise = new DeferredPromise(); + promise.complete(1); + + Event.fromPromise(promise.p)(e => { + assert.strictEqual(e, 1); + + promise = new DeferredPromise(); + promise.error(undefined); + + Event.fromPromise(promise.p)(() => { + resolve(); + }); + }); + + }); + }); + }); + suite('Relay', () => { test('should input work', () => { const e1 = new Emitter(); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 8773eff46d5..467a7c20d8a 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -7,7 +7,7 @@ import { Event } from 'vs/base/common/event'; import { firstOrDefault } from 'vs/base/common/arrays'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; @@ -24,11 +24,14 @@ import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatCo import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import product from 'vs/platform/product/common/product'; import { ActiveEditorContext } from 'vs/workbench/common/contextkeys'; +import { IViewsService } from 'vs/workbench/common/views'; +import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { KeyCode } from 'vs/base/common/keyCodes'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress.") }); @@ -36,29 +39,123 @@ const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInPr interface IVoiceChatSessionController { readonly onDidAcceptInput: Event; + readonly onDidCancelInput: Event; focusInput(): void; acceptInput(): void; updateInput(text: string): void; } -function getController(controller: InlineChatController): IVoiceChatSessionController; -function getController(context: unknown): IVoiceChatSessionController | undefined; -function getController(context: unknown): IVoiceChatSessionController | undefined { - if (context instanceof InlineChatController) { - return { - onDidAcceptInput: context.onDidAcceptInput, - focusInput: () => context.focus(), - acceptInput: () => context.acceptInput(), - updateInput: text => context.updateInput(text) - }; - } +class VoiceChatSessionControllerFactory { - if (isExecuteActionContext(context)) { - return context.widget; - } + static create(accessor: ServicesAccessor, context: 'inline'): Promise; + static create(accessor: ServicesAccessor, context: 'quick'): Promise; + static create(accessor: ServicesAccessor, context: 'view'): Promise; + static create(accessor: ServicesAccessor, context: 'focussed'): Promise; + static async create(accessor: ServicesAccessor, context: 'inline' | 'quick' | 'view' | 'focussed'): Promise { + const chatWidgetService = accessor.get(IChatWidgetService); + const chatService = accessor.get(IChatService); + const viewsService = accessor.get(IViewsService); + const chatContributionService = accessor.get(IChatContributionService); + const editorService = accessor.get(IEditorService); + const quickChatService = accessor.get(IQuickChatService); - return undefined; + // Currently Focussed Context + if (context === 'focussed') { + + // Try with the chat widget service, which currently + // only supports the chat view and quick chat + // https://github.com/microsoft/vscode/issues/191191 + const chatInput = chatWidgetService.lastFocusedWidget; + if (chatInput?.hasInputFocus()) { + return { + onDidAcceptInput: chatInput.onDidAcceptInput, + onDidCancelInput: Event.any( + // Since we do not know if the view or the quick chat + // is container of the chat input, we need to listen + // to both events here... + Event.filter(viewsService.onDidChangeViewVisibility, e => e.id === chatContributionService.getViewIdForProvider(chatInput.providerId)), + quickChatService.onDidClose + ), + focusInput: () => chatInput.focusInput(), + acceptInput: () => chatInput.acceptInput(), + updateInput: text => chatInput.updateInput(text) + }; + } + + // Try with the inline chat + const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); + if (activeCodeEditor) { + const inlineChat = InlineChatController.get(activeCodeEditor); + if (inlineChat?.hasFocus()) { + return { + onDidAcceptInput: inlineChat.onDidAcceptInput, + onDidCancelInput: inlineChat.onDidCancelInput, + focusInput: () => inlineChat.focus(), + acceptInput: () => inlineChat.acceptInput(), + updateInput: text => inlineChat.updateInput(text) + }; + } + } + } + + // View Chat + if (context === 'view') { + const provider = firstOrDefault(chatService.getProviderInfos()); + if (provider) { + const chatView = await chatWidgetService.revealViewForProvider(provider.id); + if (chatView) { + return { + onDidAcceptInput: chatView.onDidAcceptInput, + onDidCancelInput: Event.filter(viewsService.onDidChangeViewVisibility, e => e.id === chatContributionService.getViewIdForProvider(provider.id)), + focusInput: () => chatView.focusInput(), + acceptInput: () => chatView.acceptInput(), + updateInput: text => chatView.updateInput(text) + }; + } + } + } + + // Inline Chat + if (context === 'inline') { + const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); + if (activeCodeEditor) { + const inlineChat = InlineChatController.get(activeCodeEditor); + if (inlineChat) { + const inlineChatSession = inlineChat.run(); + + return { + onDidAcceptInput: inlineChat.onDidAcceptInput, + onDidCancelInput: Event.any( + inlineChat.onDidCancelInput, + Event.fromPromise(inlineChatSession) + ), + focusInput: () => inlineChat.focus(), + acceptInput: () => inlineChat.acceptInput(), + updateInput: text => inlineChat.updateInput(text) + }; + } + } + } + + // Quick Chat + if (context === 'quick') { + quickChatService.open(); + + const quickChat = chatWidgetService.lastFocusedWidget; + if (quickChat) { + return { + onDidAcceptInput: quickChat.onDidAcceptInput, + onDidCancelInput: quickChatService.onDidClose, + focusInput: () => quickChat.focusInput(), + acceptInput: () => quickChat.acceptInput(), + updateInput: text => quickChat.updateInput(text) + }; + } + } + + return undefined; + } } class VoiceChatSession { @@ -83,33 +180,41 @@ class VoiceChatSession { @IWorkbenchVoiceRecognitionService private readonly voiceRecognitionService: IWorkbenchVoiceRecognitionService ) { } - async start(context: IVoiceChatSessionController): Promise { + async start(controller: IVoiceChatSessionController): Promise { this.stop(); - this.voiceChatGettingReadyKey.set(true); + const voiceChatSessionId = ++this.voiceChatSessionIds; this.currentVoiceChatSession = new DisposableStore(); const cts = new CancellationTokenSource(); this.currentVoiceChatSession.add(toDisposable(() => cts.dispose(true))); - context.focusInput(); + this.currentVoiceChatSession.add(controller.onDidAcceptInput(() => this.stop(voiceChatSessionId))); + this.currentVoiceChatSession.add(controller.onDidCancelInput(() => this.stop(voiceChatSessionId))); + + controller.focusInput(); + + this.voiceChatGettingReadyKey.set(true); const onDidTranscribe = await this.voiceRecognitionService.transcribe(cts.token, { onDidCancel: () => this.stop(voiceChatSessionId) }); - if (cts.token.isCancellationRequested) { - return Disposable.None; - } - const voiceChatSessionId = ++this.voiceChatSessionIds; + if (cts.token.isCancellationRequested) { + return; + } this.voiceChatGettingReadyKey.set(false); this.voiceChatInProgressKey.set(true); + this.registerTranscriptionListener(controller, onDidTranscribe, this.currentVoiceChatSession); + } + + private registerTranscriptionListener(controller: IVoiceChatSessionController, onDidTranscribe: Event, disposables: DisposableStore) { let lastText: string | undefined = undefined; let lastTextSimilarCount = 0; - this.currentVoiceChatSession.add(onDidTranscribe(text => { + disposables.add(onDidTranscribe(text => { if (!text && lastText) { text = lastText; } @@ -123,17 +228,13 @@ class VoiceChatSession { } if (lastTextSimilarCount >= 2) { - context.acceptInput(); + controller.acceptInput(); } else { - context.updateInput(text); + controller.updateInput(text); } } })); - - this.currentVoiceChatSession.add(context.onDidAcceptInput(() => this.stop(voiceChatSessionId))); - - return toDisposable(() => this.stop(voiceChatSessionId)); } private isSimilarTranscription(textA: string, textB: string): boolean { @@ -151,11 +252,10 @@ class VoiceChatSession { } stop(voiceChatSessionId = this.voiceChatSessionIds): void { - if (!this.currentVoiceChatSession) { - return; - } - - if (this.voiceChatSessionIds !== voiceChatSessionId) { + if ( + !this.currentVoiceChatSession || + this.voiceChatSessionIds !== voiceChatSessionId + ) { return; } @@ -185,16 +285,11 @@ class VoiceChatInChatViewAction extends Action2 { } async run(accessor: ServicesAccessor): Promise { - const chatWidgetService = accessor.get(IChatWidgetService); - const chatService = accessor.get(IChatService); const instantiationService = accessor.get(IInstantiationService); - const provider = firstOrDefault(chatService.getProviderInfos()); - if (provider) { - const controller = await chatWidgetService.revealViewForProvider(provider.id); - if (controller) { - VoiceChatSession.getInstance(instantiationService).start(controller); - } + const controller = await VoiceChatSessionControllerFactory.create(accessor, 'view'); + if (controller) { + VoiceChatSession.getInstance(instantiationService).start(controller); } } } @@ -217,23 +312,12 @@ class InlineVoiceChatAction extends Action2 { } async run(accessor: ServicesAccessor): Promise { - const editorService = accessor.get(IEditorService); const instantiationService = accessor.get(IInstantiationService); - const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); - if (!activeCodeEditor) { - return; + const controller = await VoiceChatSessionControllerFactory.create(accessor, 'inline'); + if (controller) { + VoiceChatSession.getInstance(instantiationService).start(controller); } - - const controller = InlineChatController.get(activeCodeEditor); - if (!controller) { - return; - } - - const inlineChatSession = controller.run(); - - const disposable = await VoiceChatSession.getInstance(instantiationService).start(getController(controller)); - inlineChatSession.finally(() => disposable.dispose()); } } @@ -255,16 +339,11 @@ class QuickVoiceChatAction extends Action2 { } async run(accessor: ServicesAccessor): Promise { - const quickChatService = accessor.get(IQuickChatService); - const chatWidgetService = accessor.get(IChatWidgetService); const instantiationService = accessor.get(IInstantiationService); - quickChatService.open(); - - const controller = chatWidgetService.lastFocusedWidget; + const controller = await VoiceChatSessionControllerFactory.create(accessor, 'quick'); if (controller) { - const disposable = await VoiceChatSession.getInstance(instantiationService).start(controller); - Event.once(quickChatService.onDidClose)(() => disposable.dispose()); + VoiceChatSession.getInstance(instantiationService).start(controller); } } } @@ -296,46 +375,17 @@ class StartVoiceChatAction extends Action2 { }); } - async run(accessor: ServicesAccessor, context: unknown): Promise { - const editorService = accessor.get(IEditorService); - const chatWidgetService = accessor.get(IChatWidgetService); + async run(accessor: ServicesAccessor): Promise { const instantiationService = accessor.get(IInstantiationService); const commandService = accessor.get(ICommandService); - let controller = getController(context); - if (!controller) { - - // Without a controller, this action potentially executed from - // a global keybinding, and thus we have to find the chat - // input that is currently focussed, or have a fallback - - // 1.) a chat input widget has focus - if (chatWidgetService.lastFocusedWidget?.hasInputFocus()) { - controller = chatWidgetService.lastFocusedWidget; - } - - // 2.) a inline chat input widget has focus - if (!controller) { - const activeCodeEditor = getCodeEditor(editorService.activeTextEditorControl); - if (activeCodeEditor) { - const chatInput = InlineChatController.get(activeCodeEditor); - if (chatInput?.hasFocus()) { - controller = getController(chatInput); - } - } - } - - // 3.) open a quick chat view - if (!controller) { - return commandService.executeCommand(QuickVoiceChatAction.ID); - } + const controller = await VoiceChatSessionControllerFactory.create(accessor, 'focussed'); + if (controller) { + VoiceChatSession.getInstance(instantiationService).start(controller); + } else { + // fallback to Quick Voice Chat command + commandService.executeCommand(QuickVoiceChatAction.ID); } - - if (!controller) { - return; - } - - VoiceChatSession.getInstance(instantiationService).start(controller); } } @@ -352,6 +402,11 @@ class StopVoiceChatAction extends Action2 { }, category: CHAT_CATEGORY, f1: true, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib + 100, + when: CONTEXT_VOICE_CHAT_IN_PROGRESS, + primary: KeyCode.Escape + }, precondition: CONTEXT_VOICE_CHAT_IN_PROGRESS, icon: spinningLoading, menu: [{ diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 023fee9cfd2..1d8cacb2985 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -101,6 +101,7 @@ export class InlineChatController implements IEditorContribution { private _messages = this._store.add(new Emitter()); readonly onDidAcceptInput = Event.filter(this._messages.event, m => m === Message.ACCEPT_INPUT, this._store); + readonly onDidCancelInput = Event.filter(this._messages.event, m => m === Message.CANCEL_INPUT || m === Message.CANCEL_SESSION, this._store); private readonly _sessionStore: DisposableStore = this._store.add(new DisposableStore()); private readonly _stashedSession: MutableDisposable = this._store.add(new MutableDisposable()); From eef56ce7d3aefa1aadec4ec46f411d9e815abe3f Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 30 Aug 2023 05:44:32 -0700 Subject: [PATCH 344/607] Fix another `var()` fallback case (#191721) One more case of #190968 --- .../workbench/contrib/interactive/browser/interactiveEditor.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.css b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.css index 491aa3e8c17..d43c6a6e98b 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.css +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.css @@ -17,5 +17,5 @@ .interactive-editor .input-cell-container .monaco-editor-background, .interactive-editor .input-cell-container .margin-view-overlays { - background-color: var(--vscode-notebook-cellEditorBackground, --vscode-editor-background); + background-color: var(--vscode-notebook-cellEditorBackground, var(--vscode-editor-background)); } From bee68cee69f701e3fdcea4190ef6d14aab00fa48 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Aug 2023 15:24:37 +0200 Subject: [PATCH 345/607] allow workspace edit to "create" untitled files (#191779) https://github.com/microsoft/vscode-copilot/issues/1261 --- .../src/singlefolder-tests/workspace.test.ts | 16 ++++++++++++++++ .../contrib/bulkEdit/browser/bulkFileEdits.ts | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 6e0c8e59404..e69eecff5d1 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -977,6 +977,22 @@ suite('vscode API - workspace', () => { assert.strictEqual(document.getText(), expected); }); + + test('[Bug] Failed to create new test file when in an untitled file #1261', async function () { + const uri = vscode.Uri.parse('untitled:Untitled-5.test'); + const contents = `Hello Test File ${uri.toString()}`; + const we = new vscode.WorkspaceEdit(); + we.createFile(uri, { ignoreIfExists: true }); + we.replace(uri, new vscode.Range(0, 0, 0, 0), contents); + + const success = await vscode.workspace.applyEdit(we); + + assert.ok(success); + + const doc = await vscode.workspace.openTextDocument(uri); + assert.strictEqual(doc.getText(), contents); + }); + test('Should send a single FileWillRenameEvent instead of separate events when moving multiple files at once#111867, 1/3', async function () { const file1 = await createRandomFile(); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index c21f9e7f6c6..9f895d8b8a7 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -18,6 +18,7 @@ import { ResourceFileEdit } from 'vs/editor/browser/services/bulkEditService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { tail } from 'vs/base/common/arrays'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { Schemas } from 'vs/base/common/network'; interface IFileOperation { uris: URI[]; @@ -173,6 +174,9 @@ class CreateOperation implements IFileOperation { const undoes: DeleteEdit[] = []; for (const edit of this._edits) { + if (edit.newUri.scheme === Schemas.untitled) { + continue; // ignore, will be handled by a later edit + } if (edit.options.overwrite === undefined && edit.options.ignoreIfExists && await this._fileService.exists(edit.newUri)) { continue; // not overwriting, but ignoring, and the target file exists } From 0e3926c62a5c779c0d1ac8e267dce6bbc82624c4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Aug 2023 15:34:27 +0200 Subject: [PATCH 346/607] voice - fix bad controller when using toolbar actions (#191780) --- .../chat/electron-sandbox/actions/voiceChatActions.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 467a7c20d8a..97a1c2efcbd 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -32,6 +32,7 @@ import { IViewsService } from 'vs/workbench/common/views'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode } from 'vs/base/common/keyCodes'; +import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone.") }); const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress.") }); @@ -375,10 +376,18 @@ class StartVoiceChatAction extends Action2 { }); } - async run(accessor: ServicesAccessor): Promise { + async run(accessor: ServicesAccessor, context: unknown): Promise { const instantiationService = accessor.get(IInstantiationService); const commandService = accessor.get(ICommandService); + if (isExecuteActionContext(context)) { + // if we already get a context when the action is executed + // from a toolbar within the chat widget, then make sure + // to move focus into the input field so that the controller + // is properly retrieved + context.widget.focusInput(); + } + const controller = await VoiceChatSessionControllerFactory.create(accessor, 'focussed'); if (controller) { VoiceChatSession.getInstance(instantiationService).start(controller); From fdcc959e0a6dfc365d9a6a8a595ac76457db2f65 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 30 Aug 2023 16:43:56 +0200 Subject: [PATCH 347/607] Git - update Explorer welcome view context key (#191788) --- extensions/git/package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 105491916ca..37212410bfe 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -2950,13 +2950,13 @@ { "view": "scm", "contents": "%view.workbench.scm.folder%", - "when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == folder && scmRepositoryCount == 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && git.closedRepositoryCount == 0 && remoteName != 'codespaces'", + "when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == folder && scm.providerCount == 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && git.closedRepositoryCount == 0 && remoteName != 'codespaces'", "group": "5_scm@1" }, { "view": "scm", "contents": "%view.workbench.scm.workspace%", - "when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0 && scmRepositoryCount == 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && git.closedRepositoryCount == 0 && remoteName != 'codespaces'", + "when": "config.git.enabled && !git.missing && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0 && scm.providerCount == 0 && git.parentRepositoryCount == 0 && git.unsafeRepositoryCount == 0 && git.closedRepositoryCount == 0 && remoteName != 'codespaces'", "group": "5_scm@1" }, { @@ -2992,13 +2992,13 @@ { "view": "explorer", "contents": "%view.workbench.cloneRepository%", - "when": "config.git.enabled && git.state == initialized && scmRepositoryCount == 0", + "when": "config.git.enabled && git.state == initialized && scm.providerCount == 0", "group": "5_scm@1" }, { "view": "explorer", "contents": "%view.workbench.learnMore%", - "when": "config.git.enabled && git.state == initialized && scmRepositoryCount == 0", + "when": "config.git.enabled && git.state == initialized && scm.providerCount == 0", "group": "5_scm@10" } ] From 4538c811ebacf7eee5defab22d560dd57fdcd785 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 15:39:01 +0200 Subject: [PATCH 348/607] Fixes #191323 --- src/vs/base/common/arrays.ts | 4 + .../common/diff/advancedLinesDiffComputer.ts | 93 +++++++++++++++++-- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 22c2859f5e9..94966dc1b6f 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -666,6 +666,10 @@ export namespace CompareResult { return result < 0; } + export function isLessThanOrEqual(result: CompareResult): boolean { + return result <= 0; + } + export function isGreaterThan(result: CompareResult): boolean { return result > 0; } diff --git a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts index a7d9605d78c..55166f1ad82 100644 --- a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts @@ -3,10 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareBy, equals, findLastIndex, numberComparator, reverseOrder } from 'vs/base/common/arrays'; +import { Comparator, CompareResult, compareBy, equals, findLastIndex, numberComparator, reverseOrder } from 'vs/base/common/arrays'; import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; import { CharCode } from 'vs/base/common/charCode'; import { SetMap } from 'vs/base/common/collections'; +import { BugIndicatingError } from 'vs/base/common/errors'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { Position } from 'vs/editor/common/core/position'; @@ -302,7 +303,7 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { if (moves.length === 0) { return []; } - const joinedMoves = [moves[0]]; + let joinedMoves = [moves[0]]; for (let i = 1; i < moves.length; i++) { const last = joinedMoves[joinedMoves.length - 1]; const current = moves[i]; @@ -324,6 +325,19 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { joinedMoves.push(current); } + // Ignore non moves + const originalChanges = MonotonousFinder.createOfSorted(changes, c => c.originalRange.endLineNumberExclusive, numberComparator); + joinedMoves = joinedMoves.filter(m => { + const diffBeforeOriginalMove = originalChanges.findLastItemBeforeOrEqual(m.original.startLineNumber) + || new LineRangeMapping(new LineRange(1, 1), new LineRange(1, 1), []); + + const modifiedDistToPrevDiff = m.modified.startLineNumber - diffBeforeOriginalMove.modifiedRange.endLineNumberExclusive; + const originalDistToPrevDiff = m.original.startLineNumber - diffBeforeOriginalMove.originalRange.endLineNumberExclusive; + + const differentDistances = modifiedDistToPrevDiff !== originalDistToPrevDiff; + return differentDistances; + }); + const fullMoves = joinedMoves.map(m => { const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( m.original.toOffsetRange(), @@ -366,6 +380,60 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { } } +class MonotonousFinder { + public static create( + items: TItem[], + itemToDomain: (item: TItem) => TDomain, + domainComparator: Comparator, + ): MonotonousFinder { + items.sort((a, b) => domainComparator(itemToDomain(a), itemToDomain(b))); + return new MonotonousFinder(items, itemToDomain, domainComparator); + } + + public static createOfSorted( + items: TItem[], + itemToDomain: (item: TItem) => TDomain, + domainComparator: Comparator, + ): MonotonousFinder { + return new MonotonousFinder(items, itemToDomain, domainComparator); + } + + private _currentIdx = 0; // All values with index lower than this are smaller than or equal to _lastValue and vice versa. + private _lastValue: TDomain | undefined = undefined; // Represents a smallest value. + private _hasLastValue = false; + + private constructor( + private readonly _items: TItem[], + private readonly _itemToDomain: (item: TItem) => TDomain, + private readonly _domainComparator: Comparator, + ) { + } + + /** + * Assumes the values are monotonously increasing. + */ + findLastItemBeforeOrEqual(value: TDomain): TItem | undefined { + if (this._hasLastValue && CompareResult.isLessThan(this._domainComparator(value, this._lastValue!))) { + // Values must be monotonously increasing + throw new BugIndicatingError(); + } + this._lastValue = value; + this._hasLastValue = true; + + while ( + this._currentIdx < this._items.length + && CompareResult.isLessThanOrEqual(this._domainComparator( + this._itemToDomain(this._items[this._currentIdx]), + value + )) + ) { + this._currentIdx++; + } + + return this._currentIdx === 0 ? undefined : this._items[this._currentIdx - 1]; + } +} + function intersectRanges(ranges1: LineRange[], ranges2: LineRange[]): LineRange[] { const result: LineRange[] = []; @@ -839,16 +907,15 @@ export class LinesSliceCharSequence implements ISequence { } public extendToFullLines(range: OffsetRange): OffsetRange { - const firstIdx = findLastIdxMonotonous(this.firstCharOffsetByLineMinusOne, x => x <= range.start); - const lastIdx = findFirstIdxMonotonous(this.firstCharOffsetByLineMinusOne, x => range.endExclusive <= x); - - const start = firstIdx === -1 ? 0 : this.firstCharOffsetByLineMinusOne[firstIdx]; - const end = lastIdx === this.firstCharOffsetByLineMinusOne.length ? this.elements.length : this.firstCharOffsetByLineMinusOne[lastIdx]; + const start = findLastMonotonous(this.firstCharOffsetByLineMinusOne, x => x <= range.start) ?? 0; + const end = findFirstMonotonous(this.firstCharOffsetByLineMinusOne, x => range.endExclusive <= x) ?? this.elements.length; return new OffsetRange(start, end); } } /** + * `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! + * * @returns -1 if predicate is false for all items */ function findLastIdxMonotonous(arr: T[], predicate: (item: T) => boolean): number { @@ -865,7 +932,14 @@ function findLastIdxMonotonous(arr: T[], predicate: (item: T) => boolean): nu return i - 1; } +export function findLastMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { + const idx = findLastIdxMonotonous(arr, predicate); + return idx === -1 ? undefined : arr[idx]; +} + /** + * `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`! + * * @returns arr.length if predicate is false for all items */ function findFirstIdxMonotonous(arr: T[], predicate: (item: T) => boolean): number { @@ -882,6 +956,11 @@ function findFirstIdxMonotonous(arr: T[], predicate: (item: T) => boolean): n return i; } +export function findFirstMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { + const idx = findFirstIdxMonotonous(arr, predicate); + return idx === arr.length ? undefined : arr[idx]; +} + function isWordChar(charCode: number): boolean { return charCode >= CharCode.a && charCode <= CharCode.z || charCode >= CharCode.A && charCode <= CharCode.Z From ec9aa5cfdf18ac4f5d3fb2b384869dca46f6451f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 30 Aug 2023 16:50:40 +0200 Subject: [PATCH 349/607] Revert "fix #190228 (#191207)" (#191789) This reverts commit cafcb59c16b5fcb2ae2db76cea0ba2596df8b7db. --- .../workbench/contrib/extensions/browser/extensionEditor.ts | 6 ++++-- .../contrib/extensions/browser/media/extensionEditor.css | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 0cb4cdeab43..16a37dc27c9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -855,10 +855,12 @@ export class ExtensionEditor extends EditorPane { extensionPackReadme.style.maxWidth = '882px'; const extensionPack = append(extensionPackReadme, $('div', { class: 'extension-pack' })); - if (manifest.extensionPack!.length < 3) { + if (manifest.extensionPack!.length <= 3) { extensionPackReadme.classList.add('one-row'); - } else if (manifest.extensionPack!.length < 5) { + } else if (manifest.extensionPack!.length <= 6) { extensionPackReadme.classList.add('two-rows'); + } else if (manifest.extensionPack!.length <= 9) { + extensionPackReadme.classList.add('three-rows'); } else { extensionPackReadme.classList.add('more-rows'); } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 575e6870b54..174b332538b 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -517,6 +517,10 @@ height: 224px; } +.extension-editor > .body > .content > .details > .readme-container > .extension-pack-readme.three-rows > .extension-pack { + height: 306px; +} + .extension-editor > .body > .content > .details > .readme-container > .extension-pack-readme.more-rows > .extension-pack { height: 326px; } From 6b9018f059cbe1d8a9ea75c3b026d0a85e27eb5b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 08:06:33 -0700 Subject: [PATCH 350/607] Workaround slow update webgl issue on Windows Fixes #190195 --- .../terminal/browser/xterm/xtermTerminal.ts | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index fcf7a078831..7d968e8818f 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -43,6 +43,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { debounce } from 'vs/base/common/decorators'; import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; +import { isWindows } from 'vs/base/common/platform'; const enum RenderConstants { /** @@ -120,6 +121,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID readonly raw: RawXtermTerminal; private _core: IXtermCore; private static _suggestedRendererType: 'canvas' | 'dom' | undefined = undefined; + private static _checkedWebglCompatible = false; private _attached?: { container: HTMLElement; options: IXtermAttachToElementOptions }; private _isPhysicalMouseWheel = MouseWheelClassifier.INSTANCE.isPhysicalMouseWheel(); @@ -677,6 +679,25 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID if (!this.raw.element || this._webglAddon) { return; } + + // Check if the the WebGL renderer is compatible with xterm.js: + // - https://github.com/microsoft/vscode/issues/190195 + // - https://github.com/xtermjs/xterm.js/issues/4665 + // - https://bugs.chromium.org/p/chromium/issues/detail?id=1476475 + if (!XtermTerminal._checkedWebglCompatible && isWindows) { + XtermTerminal._checkedWebglCompatible = true; + const checkCanvas = document.createElement('canvas'); + const checkGl = checkCanvas.getContext('webgl2'); + const debugInfo = checkGl?.getExtension('WEBGL_debug_renderer_info'); + if (checkGl && debugInfo) { + const renderer = checkGl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL); + if (renderer.startsWith('ANGLE (Google, Vulkan 1.3.0 (SwiftShader Device (Subzero)')) { + this._disableWebglForThisSession(); + return; + } + } + } + const Addon = await this._getWebglAddonConstructor(); this._webglAddon = new Addon(); this._disposeOfCanvasRenderer(); @@ -701,12 +722,16 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID if (!neverMeasureRenderTime && this._configHelper.config.gpuAcceleration !== 'off') { this._measureRenderTime(); } - XtermTerminal._suggestedRendererType = 'canvas'; - this._disposeOfWebglRenderer(); - this._enableCanvasRenderer(); + this._disableWebglForThisSession(); } } + private _disableWebglForThisSession() { + XtermTerminal._suggestedRendererType = 'canvas'; + this._disposeOfWebglRenderer(); + this._enableCanvasRenderer(); + } + private async _enableCanvasRenderer(): Promise { if (!this.raw.element || this._canvasAddon) { return; From 5cc83f79943d1de0abefa00d650a2533e30be99a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 30 Aug 2023 08:20:08 -0700 Subject: [PATCH 351/607] cli: verify vscode server integrity before committing to cache (#191792) Fixes #191469 --- cli/src/tunnels/code_server.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index 5bc5e39514a..16655533754 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -14,7 +14,7 @@ use crate::tunnels::paths::{get_server_folder_name, SERVER_FOLDER_NAME}; use crate::update_service::{ unzip_downloaded_release, Platform, Release, TargetKind, UpdateService, }; -use crate::util::command::{capture_command, kill_tree}; +use crate::util::command::{capture_command, capture_command_and_check_status, kill_tree}; use crate::util::errors::{wrap, AnyError, CodeError, ExtensionInstallFailed, WrappedError}; use crate::util::http::{self, BoxedHttp}; use crate::util::io::SilentCopyProgress; @@ -416,11 +416,23 @@ impl<'a> ServerBuilder<'a> { ) .await?; - unzip_downloaded_release( - &archive_path, - &target_dir.join(SERVER_FOLDER_NAME), - SilentCopyProgress(), - )?; + let server_dir = target_dir.join(SERVER_FOLDER_NAME); + unzip_downloaded_release(&archive_path, &server_dir, SilentCopyProgress())?; + + let output = capture_command_and_check_status( + server_dir + .join("bin") + .join(self.server_params.release.quality.server_entrypoint()), + &["--version"], + ) + .await + .map_err(|e| wrap(e, "error checking server integrity"))?; + + trace!( + self.logger, + "Server integrity verified, version: {}", + String::from_utf8_lossy(&output.stdout).replace('\n', " / ") + ); Ok(()) }) From d46d86dd75cd99f652a183e02a091dbae31d5164 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Aug 2023 17:36:04 +0200 Subject: [PATCH 352/607] voice - replace codicon when hovering over stop button (#191777) --- .../actions/media/voiceChatActions.css | 14 ++++++++++++++ .../electron-sandbox/actions/voiceChatActions.ts | 1 + 2 files changed, 15 insertions(+) create mode 100644 src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css b/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css new file mode 100644 index 00000000000..53e4022e443 --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover, +.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover { + animation: none; /* stop the running voice recording animation for showing another codicon to stop */ +} + +.monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover::before, +.monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover::before { + content: "\eba5"; /* use `stop-circle` icon unicode for hovering over running voice recording */ +} diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 97a1c2efcbd..792071aab04 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/voiceChatActions'; import { Event } from 'vs/base/common/event'; import { firstOrDefault } from 'vs/base/common/arrays'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; From 8813aca70522f4e5e4b3996db46dac2cb921564b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 30 Aug 2023 08:37:00 -0700 Subject: [PATCH 353/607] cli: recycle all tunnels the cli creates for all scenarios (#191800) Fixes #191749 --- cli/src/tunnels/dev_tunnels.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/cli/src/tunnels/dev_tunnels.rs b/cli/src/tunnels/dev_tunnels.rs index b77f6da5f2e..e7b84ed4112 100644 --- a/cli/src/tunnels/dev_tunnels.rs +++ b/cli/src/tunnels/dev_tunnels.rs @@ -212,6 +212,7 @@ impl ActiveTunnel { const VSCODE_CLI_TUNNEL_TAG: &str = "vscode-server-launcher"; const VSCODE_CLI_FORWARDING_TAG: &str = "vscode-port-forward"; +const OWNED_TUNNEL_TAGS: &[&str] = &[VSCODE_CLI_TUNNEL_TAG, VSCODE_CLI_FORWARDING_TAG]; const MAX_TUNNEL_NAME_LENGTH: usize = 20; fn get_host_token_from_tunnel(tunnel: &Tunnel) -> String { @@ -635,7 +636,7 @@ impl DevTunnels { "Tunnel limit hit, trying to recycle an old tunnel" ); - let existing_tunnels = self.list_all_server_tunnels().await?; + let existing_tunnels = self.list_tunnels_with_tag(OWNED_TUNNEL_TAGS).await?; let recyclable = existing_tunnels .iter() @@ -667,13 +668,15 @@ impl DevTunnels { } } - async fn list_all_server_tunnels(&mut self) -> Result, AnyError> { + async fn list_tunnels_with_tag( + &mut self, + tags: &[&'static str], + ) -> Result, AnyError> { let tunnels = spanf!( self.log, self.log.span("dev-tunnel.listall"), self.client.list_all_tunnels(&TunnelRequestOptions { - tags: vec![self.tag.to_string()], - require_all_tags: true, + tags: tags.iter().map(|t| t.to_string()).collect(), ..Default::default() }) ) @@ -711,7 +714,7 @@ impl DevTunnels { preferred_name: Option<&str>, mut use_random_name: bool, ) -> Result { - let existing_tunnels = self.list_all_server_tunnels().await?; + let existing_tunnels = self.list_tunnels_with_tag(&[self.tag]).await?; let is_name_free = |n: &str| { !existing_tunnels.iter().any(|v| { v.status From afa0d9fd750cdeaf6c03504fb84e8603eea0cc2e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Aug 2023 17:47:42 +0200 Subject: [PATCH 354/607] voice - make stop icon more explicit --- .../chat/electron-sandbox/actions/media/voiceChatActions.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css b/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css index 53e4022e443..b081c2c8d2f 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/media/voiceChatActions.css @@ -10,5 +10,5 @@ .monaco-workbench .interactive-input-part .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover::before, .monaco-workbench .inline-chat .monaco-action-bar .action-label.codicon-loading.codicon-modifier-spin:not(.disabled):hover::before { - content: "\eba5"; /* use `stop-circle` icon unicode for hovering over running voice recording */ + content: "\ead7"; /* use `debug-stop` icon unicode for hovering over running voice recording */ } From 1e4020e70c32d3f836f44ce816bbca6f761743be Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Aug 2023 08:50:59 -0700 Subject: [PATCH 355/607] set default focusAfterRun to none --- .../contrib/terminal/common/terminalConfiguration.ts | 5 ++--- .../browser/terminal.accessibility.contribution.ts | 8 +++----- .../accessibility/browser/terminalAccessibilityHelp.ts | 3 ++- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index 1f8820b634f..dde5c45fd5c 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -611,11 +611,10 @@ const terminalConfiguration: IConfigurationNode = { }, [TerminalSettingId.FocusAfterRun]: { markdownDescription: localize('terminal.integrated.focusAfterRun', "Controls whether the terminal, accessible buffer, or neither will be focused after `Terminal: Run Selected Text In Active Terminal` has been run."), - enum: ['auto', 'terminal', 'accessible-buffer', 'none'], - default: 'auto', + enum: ['terminal', 'accessible-buffer', 'none'], + default: 'none', tags: ['accessibility'], markdownEnumDescriptions: [ - localize('terminal.integrated.focusAfterRun.auto', "Set to `terminal` when in screen reader optimized mode and `none` otherwise."), localize('terminal.integrated.focusAfterRun.terminal', "Always focus the terminal."), localize('terminal.integrated.focusAfterRun.accessible-buffer', "Always focus the accessible buffer."), localize('terminal.integrated.focusAfterRun.none', "Do nothing."), diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 8c3ef5b48eb..682d14ccde1 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -6,7 +6,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; -import { CONTEXT_ACCESSIBILITY_MODE_ENABLED, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -61,14 +61,12 @@ class AccessibleBufferContribution extends DisposableStore implements ITerminalC processManager: ITerminalProcessManager, widgetManager: TerminalWidgetManager, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IConfigurationService configurationService: IConfigurationService, - @IAccessibilityService accessibilityService: IAccessibilityService + @IConfigurationService configurationService: IConfigurationService ) { super(); this.add(_instance.onDidRunText(() => { const focusAfterRun = configurationService.getValue(TerminalSettingId.FocusAfterRun); - const focusTerminal = focusAfterRun === 'terminal' || (focusAfterRun === 'auto' && accessibilityService.isScreenReaderOptimized()); - if (focusTerminal) { + if (focusAfterRun === 'terminal') { _instance.focus(true); } else if (focusAfterRun === 'accessible-buffer') { this.show(); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 9205be2e624..340427fd528 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -11,7 +11,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ShellIntegrationStatus, WindowsShellType } from 'vs/platform/terminal/common/terminal'; +import { ShellIntegrationStatus, TerminalSettingId, WindowsShellType } from 'vs/platform/terminal/common/terminal'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { AccessibleViewType, IAccessibleContentProvider, IAccessibleViewOptions } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -93,6 +93,7 @@ export class TerminalAccessibleContentProvider extends Disposable implements IAc } content.push(this._descriptionForCommand(TerminalCommandId.OpenDetectedLink, localize('openDetectedLink', 'The Open Detected Link ({0}) command enables screen readers to easily open links found in the terminal.'), localize('openDetectedLinkNoKb', 'The Open Detected Link command enables screen readers to easily open links found in the terminal and is currently not triggerable by a keybinding.'))); content.push(this._descriptionForCommand(TerminalCommandId.NewWithProfile, localize('newWithProfile', 'The Create New Terminal (With Profile) ({0}) command allows for easy terminal creation using a specific profile.'), localize('newWithProfileNoKb', 'The Create New Terminal (With Profile) command allows for easy terminal creation using a specific profile and is currently not triggerable by a keybinding.'))); + content.push(localize('focusAfterRun', 'Configure what gets focused after running selected text in the terminal with `{0}`.', TerminalSettingId.FocusAfterRun)); content.push(localize('accessibilitySettings', 'Access accessibility settings such as `terminal.integrated.tabFocusMode` via the Preferences: Open Accessibility Settings command.')); return content.join('\n\n'); } From 65dd28d745a5369653196d67b83b0390cd2a82bf Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 30 Aug 2023 17:53:35 +0200 Subject: [PATCH 356/607] voice - add a new action to stop and accept voice input (#191802) --- .../actions/voiceChatActions.ts | 87 ++++++++++++++----- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 792071aab04..05e454d36a2 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -160,21 +160,26 @@ class VoiceChatSessionControllerFactory { } } -class VoiceChatSession { +interface ActiveVoiceChatSession { + readonly controller: IVoiceChatSessionController; + readonly disposables: DisposableStore; +} - private static instance: VoiceChatSession | undefined = undefined; - static getInstance(instantiationService: IInstantiationService): VoiceChatSession { - if (!VoiceChatSession.instance) { - VoiceChatSession.instance = instantiationService.createInstance(VoiceChatSession); +class VoiceChatSessions { + + private static instance: VoiceChatSessions | undefined = undefined; + static getInstance(instantiationService: IInstantiationService): VoiceChatSessions { + if (!VoiceChatSessions.instance) { + VoiceChatSessions.instance = instantiationService.createInstance(VoiceChatSessions); } - return VoiceChatSession.instance; + return VoiceChatSessions.instance; } private voiceChatInProgressKey = CONTEXT_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); private voiceChatGettingReadyKey = CONTEXT_VOICE_CHAT_GETTING_READY.bindTo(this.contextKeyService); - private currentVoiceChatSession: DisposableStore | undefined = undefined; + private currentVoiceChatSession: ActiveVoiceChatSession | undefined = undefined; private voiceChatSessionIds = 0; constructor( @@ -186,14 +191,18 @@ class VoiceChatSession { this.stop(); const voiceChatSessionId = ++this.voiceChatSessionIds; - this.currentVoiceChatSession = new DisposableStore(); + this.currentVoiceChatSession = { + controller, + disposables: new DisposableStore() + }; const cts = new CancellationTokenSource(); - this.currentVoiceChatSession.add(toDisposable(() => cts.dispose(true))); + this.currentVoiceChatSession.disposables.add(toDisposable(() => cts.dispose(true))); - this.currentVoiceChatSession.add(controller.onDidAcceptInput(() => this.stop(voiceChatSessionId))); - this.currentVoiceChatSession.add(controller.onDidCancelInput(() => this.stop(voiceChatSessionId))); + this.currentVoiceChatSession.disposables.add(controller.onDidAcceptInput(() => this.stop(voiceChatSessionId))); + this.currentVoiceChatSession.disposables.add(controller.onDidCancelInput(() => this.stop(voiceChatSessionId))); + controller.updateInput(''); controller.focusInput(); this.voiceChatGettingReadyKey.set(true); @@ -209,14 +218,14 @@ class VoiceChatSession { this.voiceChatGettingReadyKey.set(false); this.voiceChatInProgressKey.set(true); - this.registerTranscriptionListener(controller, onDidTranscribe, this.currentVoiceChatSession); + this.registerTranscriptionListener(this.currentVoiceChatSession, onDidTranscribe); } - private registerTranscriptionListener(controller: IVoiceChatSessionController, onDidTranscribe: Event, disposables: DisposableStore) { + private registerTranscriptionListener(session: ActiveVoiceChatSession, onDidTranscribe: Event) { let lastText: string | undefined = undefined; let lastTextSimilarCount = 0; - disposables.add(onDidTranscribe(text => { + session.disposables.add(onDidTranscribe(text => { if (!text && lastText) { text = lastText; } @@ -230,9 +239,9 @@ class VoiceChatSession { } if (lastTextSimilarCount >= 2) { - controller.acceptInput(); + session.controller.acceptInput(); } else { - controller.updateInput(text); + session.controller.updateInput(text); } } @@ -261,12 +270,23 @@ class VoiceChatSession { return; } - this.currentVoiceChatSession.dispose(); + this.currentVoiceChatSession.disposables.dispose(); this.currentVoiceChatSession = undefined; this.voiceChatGettingReadyKey.set(false); this.voiceChatInProgressKey.set(false); } + + accept(voiceChatSessionId = this.voiceChatSessionIds): void { + if ( + !this.currentVoiceChatSession || + this.voiceChatSessionIds !== voiceChatSessionId + ) { + return; + } + + this.currentVoiceChatSession.controller.acceptInput(); + } } class VoiceChatInChatViewAction extends Action2 { @@ -291,7 +311,7 @@ class VoiceChatInChatViewAction extends Action2 { const controller = await VoiceChatSessionControllerFactory.create(accessor, 'view'); if (controller) { - VoiceChatSession.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller); } } } @@ -318,7 +338,7 @@ class InlineVoiceChatAction extends Action2 { const controller = await VoiceChatSessionControllerFactory.create(accessor, 'inline'); if (controller) { - VoiceChatSession.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller); } } } @@ -345,7 +365,7 @@ class QuickVoiceChatAction extends Action2 { const controller = await VoiceChatSessionControllerFactory.create(accessor, 'quick'); if (controller) { - VoiceChatSession.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller); } } } @@ -391,7 +411,7 @@ class StartVoiceChatAction extends Action2 { const controller = await VoiceChatSessionControllerFactory.create(accessor, 'focussed'); if (controller) { - VoiceChatSession.getInstance(instantiationService).start(controller); + VoiceChatSessions.getInstance(instantiationService).start(controller); } else { // fallback to Quick Voice Chat command commandService.executeCommand(QuickVoiceChatAction.ID); @@ -434,7 +454,29 @@ class StopVoiceChatAction extends Action2 { } run(accessor: ServicesAccessor): void { - VoiceChatSession.getInstance(accessor.get(IInstantiationService)).stop(); + VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).stop(); + } +} + +class StopVoiceChatAndSubmitAction extends Action2 { + + static readonly ID = 'workbench.action.chat.stopVoiceChatAndSubmit'; + + constructor() { + super({ + id: StopVoiceChatAndSubmitAction.ID, + title: { + value: localize('workbench.action.chat.stopAndAcceptVoiceChat.label', "Stop Voice Chat and Submit"), + original: 'Stop Voice Chat and Submit' + }, + category: CHAT_CATEGORY, + f1: true, + precondition: CONTEXT_VOICE_CHAT_IN_PROGRESS + }); + } + + run(accessor: ServicesAccessor): void { + VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).accept(); } } @@ -446,5 +488,6 @@ export function registerVoiceChatActions() { registerAction2(StartVoiceChatAction); registerAction2(StopVoiceChatAction); + registerAction2(StopVoiceChatAndSubmitAction); } } From 3ea4d66a5b943aafd6585fc546c2899958093f29 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 30 Aug 2023 18:04:52 +0200 Subject: [PATCH 357/607] changing to use code action instead of setting --- .../client/src/jsonClient.ts | 32 +++++++------------ .../json-language-features/package.json | 6 ---- .../json-language-features/package.nls.json | 1 - 3 files changed, 12 insertions(+), 27 deletions(-) diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 23410ebb814..a801245a46d 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -8,7 +8,7 @@ export type JSONLanguageStatus = { schemas: string[] }; import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, ColorInformation, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, TextEditorOptions + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, CodeActionKind, CodeAction } from 'vscode'; import { LanguageClientOptions, RequestType, NotificationType, FormattingOptions as LSPFormattingOptions, @@ -102,7 +102,6 @@ export type JSONSchemaSettings = { export namespace SettingIds { export const enableFormatter = 'json.format.enable'; export const enableKeepLines = 'json.format.keepLines'; - export const enableSortOnSave = 'json.sortOnSave.enable'; export const enableValidation = 'json.validate.enable'; export const enableSchemaDownload = 'json.schemaDownload.enable'; export const maxItemsComputed = 'json.maxItemsComputed'; @@ -171,15 +170,6 @@ export async function startClient(context: ExtensionContext, newLanguageClient: window.showInformationMessage(l10n.t('JSON schema cache cleared.')); })); - toDispose.push(workspace.onWillSaveTextDocument(event => { - const sortOnSave = workspace.getConfiguration().get(SettingIds.enableSortOnSave); - const document = event.document; - if (sortOnSave && (document.languageId === 'json' || document.languageId === 'jsonc')) { - const documentOptions = getOptionsForDocument(document); - const textEditsPromise = getSortTextEdits(document, documentOptions?.tabSize, documentOptions?.insertSpaces); - event.waitUntil(textEditsPromise); - } - })); toDispose.push(commands.registerCommand('json.sort', async () => { @@ -312,6 +302,17 @@ export async function startClient(context: ExtensionContext, newLanguageClient: return r.then(checkLimit); } return checkLimit(r); + }, + provideCodeActions(doc) { + console.log('doc : ', doc); + console.log('inside of provideCodeActions'); + const codeActions: CodeAction[] = []; + const sortCodeAction = new CodeAction('Sort JSON', CodeActionKind.Source); + sortCodeAction.command = { + command: 'json.sort', + title: 'Sort JSON' + }; + return codeActions; } } }; @@ -643,12 +644,3 @@ function updateMarkdownString(h: MarkdownString): MarkdownString { function isSchemaResolveError(d: Diagnostic) { return d.code === /* SchemaResolveError */ 0x300; } - -function getOptionsForDocument(document: TextDocument): TextEditorOptions | undefined { - for (const editor of window.visibleTextEditors) { - if (editor.document.uri.toString() === document.uri.toString()) { - return editor.options; - } - } - return; -} diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index cd9e69d69b4..f804c30da79 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -91,12 +91,6 @@ "default": false, "description": "%json.format.keepLines.desc%" }, - "json.sortOnSave.enable": { - "type": "boolean", - "scope": "window", - "default": false, - "description": "%json.sortOnSave.enable.desc%" - }, "json.trace.server": { "type": "string", "scope": "window", diff --git a/extensions/json-language-features/package.nls.json b/extensions/json-language-features/package.nls.json index df68b3f8eac..2586bc6ab0a 100644 --- a/extensions/json-language-features/package.nls.json +++ b/extensions/json-language-features/package.nls.json @@ -8,7 +8,6 @@ "json.schemas.schema.desc": "The schema definition for the given URL. The schema only needs to be provided to avoid accesses to the schema URL.", "json.format.enable.desc": "Enable/disable default JSON formatter", "json.format.keepLines.desc" : "Keep all existing new lines when formatting.", - "json.sortOnSave.enable.desc": "Enable/disable default sorting on save", "json.validate.enable.desc": "Enable/disable JSON validation.", "json.tracing.desc": "Traces the communication between VS Code and the JSON language server.", "json.colorDecorators.enable.desc": "Enables or disables color decorators", From 49581af0aa7abbad69699899dadb972488998c32 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:17:45 -0700 Subject: [PATCH 358/607] Disable renderer unit test on Windows --- .../terminal/test/browser/xterm/xtermTerminal.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 ab689e76e87..578d5b7fbf5 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 @@ -33,6 +33,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { Color, RGBA } from 'vs/base/common/color'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { isWindows } from 'vs/base/common/platform'; class TestWebglAddon implements WebglAddon { static shouldThrow = false; @@ -256,7 +257,9 @@ suite('XtermTerminal', () => { }); suite('renderers', () => { - test('should re-evaluate gpu acceleration auto when the setting is changed', async () => { + // This is skipped on Windows because the result depends on the webgl + // renderer in the browsing context + (isWindows ? test.skip : test)('should re-evaluate gpu acceleration auto when the setting is changed', async () => { // Check initial state strictEqual(TestWebglAddon.isEnabled, false); From e2d858ecb03625377d4ddfaf9b7d65061745a0c9 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 30 Aug 2023 09:32:45 -0700 Subject: [PATCH 359/607] changed command title and name --- extensions/ipynb/package.json | 6 +++--- extensions/ipynb/package.nls.json | 2 +- .../notebook/browser/controller/cellOutputActions.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index f5e25ac3695..25d08040183 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -58,8 +58,8 @@ "title": "%cleanInvalidImageAttachment.title%" }, { - "command": "notebook.cellOutput.copyToClipboard", - "title": "%copyOutputToClipboard.title%" + "command": "notebook.cellOutput.copy", + "title": "%copyCellOutput.title%" } ], "notebooks": [ @@ -106,7 +106,7 @@ ], "webview/context": [ { - "command": "notebook.cellOutput.copyToClipboard", + "command": "notebook.cellOutput.copy", "when": "webviewId == 'notebook.output' && webviewSection == 'image'" } ] diff --git a/extensions/ipynb/package.nls.json b/extensions/ipynb/package.nls.json index 45aa2aa03e8..1f281c32dd3 100644 --- a/extensions/ipynb/package.nls.json +++ b/extensions/ipynb/package.nls.json @@ -6,7 +6,7 @@ "newUntitledIpynb.shortTitle": "Jupyter Notebook", "openIpynbInNotebookEditor.title": "Open IPYNB File In Notebook Editor", "cleanInvalidImageAttachment.title": "Clean Invalid Image Attachment Reference", - "copyOutputToClipboard.title": "Copy Output to Clipboard", + "copyCellOutput.title": "Copy Output", "markdownAttachmentRenderer.displayName": { "message": "Markdown-It ipynb Cell Attachment renderer", "comment": [ diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index 956222d7a36..529fb143978 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -17,13 +17,13 @@ import { ICellOutputViewModel, ICellViewModel, INotebookEditor, getNotebookEdito import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copyToClipboard'; +export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copy'; registerAction2(class CopyCellOutputAction extends Action2 { constructor() { super({ id: COPY_OUTPUT_COMMAND_ID, - title: localize('notebookActions.copyOutput', "Copy Output to Clipboard"), + title: localize('notebookActions.copyOutput', "Copy Output"), menu: { id: MenuId.NotebookOutputToolbar, when: NOTEBOOK_CELL_HAS_OUTPUTS From de94adc47511b51dd239381e4c34e16dfa870d9e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:33:14 -0700 Subject: [PATCH 360/607] Update distro Fixes #191605 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0af191524ed..1a58592be0a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "c7d53f94cfb25168c3c3ae9a6f4902d32643a0ee", + "distro": "021e674d5265eb9125cfc0282c3a9a6091f4982d", "author": { "name": "Microsoft Corporation" }, From 29137c69f1243d31302e57e26c2ea169b1c6254e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 30 Aug 2023 18:46:39 +0200 Subject: [PATCH 361/607] make sure codeEditor is set when bulk editing via diff editor (#191810) re https://github.com/microsoft/vscode/issues/188385 --- src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index 1258f618970..888c67f46a9 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -8,7 +8,7 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; import { ResourceMap, ResourceSet } from 'vs/base/common/map'; import { URI } from 'vs/base/common/uri'; -import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; import { IBulkEditOptions, IBulkEditPreviewHandler, IBulkEditResult, IBulkEditService, ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { WorkspaceEdit } from 'vs/editor/common/languages'; @@ -197,6 +197,8 @@ export class BulkEditService implements IBulkEditService { const candidate = this._editorService.activeTextEditorControl; if (isCodeEditor(candidate)) { codeEditor = candidate; + } else if (isDiffEditor(candidate)) { + codeEditor = candidate.getModifiedEditor(); } } From cc5e466c96635ef3e0d18896b6054eb51fe5969a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:47:27 -0700 Subject: [PATCH 362/607] Remove windows check --- .../workbench/contrib/terminal/browser/xterm/xtermTerminal.ts | 3 +-- 1 file changed, 1 insertion(+), 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 7d968e8818f..12aaa0b0bb6 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -43,7 +43,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { debounce } from 'vs/base/common/decorators'; import { MouseWheelClassifier } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { IMouseWheelEvent, StandardWheelEvent } from 'vs/base/browser/mouseEvent'; -import { isWindows } from 'vs/base/common/platform'; const enum RenderConstants { /** @@ -684,7 +683,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID // - https://github.com/microsoft/vscode/issues/190195 // - https://github.com/xtermjs/xterm.js/issues/4665 // - https://bugs.chromium.org/p/chromium/issues/detail?id=1476475 - if (!XtermTerminal._checkedWebglCompatible && isWindows) { + if (!XtermTerminal._checkedWebglCompatible) { XtermTerminal._checkedWebglCompatible = true; const checkCanvas = document.createElement('canvas'); const checkGl = checkCanvas.getContext('webgl2'); From ed06154e36dba10a6625f33c4c2c6626df54d57a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:49:19 -0700 Subject: [PATCH 363/607] Disable test --- .../terminal/test/browser/xterm/xtermTerminal.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 578d5b7fbf5..f4c0afd0259 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 @@ -257,9 +257,9 @@ suite('XtermTerminal', () => { }); suite('renderers', () => { - // This is skipped on Windows because the result depends on the webgl - // renderer in the browsing context - (isWindows ? test.skip : test)('should re-evaluate gpu acceleration auto when the setting is changed', async () => { + // This is skipped until the webgl renderer bug is fixed in Chromium + // https://bugs.chromium.org/p/chromium/issues/detail?id=1476475 + test.skip('should re-evaluate gpu acceleration auto when the setting is changed', async () => { // Check initial state strictEqual(TestWebglAddon.isEnabled, false); From cd7388f4dad4c7102713db8ebb16488f070433ea Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 30 Aug 2023 09:50:50 -0700 Subject: [PATCH 364/607] forwarding: fix formatting issues in the log (#191814) Fixes #191759 --- extensions/tunnel-forwarding/src/split.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/tunnel-forwarding/src/split.ts b/extensions/tunnel-forwarding/src/split.ts index 6e9d7474604..33ad055ac67 100644 --- a/extensions/tunnel-forwarding/src/split.ts +++ b/extensions/tunnel-forwarding/src/split.ts @@ -9,6 +9,8 @@ export const splitNewLines = () => new StreamSplitter('\n'.charCodeAt(0)); /** * Copied and simplified from src\vs\base\node\nodeStreams.ts + * + * Exception: does not include the split character in the output. */ export class StreamSplitter extends Transform { private buffer: Buffer | undefined; @@ -31,7 +33,7 @@ export class StreamSplitter extends Transform { break; } - this.push(this.buffer.subarray(offset, index + 1)); + this.push(this.buffer.subarray(offset, index)); offset = index + 1; } From d3c20779626942e64713fefeb1f84b363946dd94 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 09:51:35 -0700 Subject: [PATCH 365/607] Fix import --- .../contrib/terminal/test/browser/xterm/xtermTerminal.test.ts | 1 - 1 file changed, 1 deletion(-) 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 f4c0afd0259..204505dd1e6 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 @@ -33,7 +33,6 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { Color, RGBA } from 'vs/base/common/color'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; -import { isWindows } from 'vs/base/common/platform'; class TestWebglAddon implements WebglAddon { static shouldThrow = false; From 4dc543d246c6cb2987904444863ea0a51102a95a Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Aug 2023 09:55:19 -0700 Subject: [PATCH 366/607] fix #188329 --- .../workbench/contrib/terminal/browser/terminalInstance.ts | 5 +++++ .../accessibility/browser/terminalAccessibleWidget.ts | 5 ----- 2 files 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 449e9fce543..64bd477b0e8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -976,6 +976,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; } + if (event.key === 'Tab' && event.shiftKey) { + event.preventDefault(); + return true; + } + // Always have alt+F4 skip the terminal on Windows and allow it to be handled by the // system if (isWindows && event.altKey && event.key === 'F4' && !event.ctrlKey) { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts index 6b43d58d553..5190803c2d0 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts @@ -118,11 +118,6 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { // On escape, hide the accessible buffer and force focus onto the terminal this.hide(true); break; - case KeyCode.Tab: - // On tab or shift+tab, hide the accessible buffer and perform the default tab - // behavior - this.hide(); - break; } })); this.add(this._editorWidget.onDidFocusEditorText(async () => { From c394fb8959fcaf1f6e9831a0b2b19da7cd4e1286 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 30 Aug 2023 10:02:48 -0700 Subject: [PATCH 367/607] cli: polish serve-web help (#191817) Fixes #191601 --- cli/src/commands/args.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index bfa1c6f2da4..cbc33fcb071 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -52,6 +52,7 @@ const VERSION: &str = concatcp!(NUMBER_IN_VERSION, " (commit ", COMMIT_IN_VERSIO #[clap( help_template = INTEGRATED_TEMPLATE, long_about = None, + name = constants::APPLICATION_NAME, version = VERSION, )] pub struct IntegratedCli { @@ -84,6 +85,7 @@ pub struct CliCore { help_template = STANDALONE_TEMPLATE, long_about = None, version = VERSION, + name = constants::APPLICATION_NAME, )] pub struct StandaloneCli { #[clap(flatten)] @@ -173,6 +175,7 @@ pub enum Commands { Version(VersionArgs), /// Runs a local web version of VS Code. + #[clap(about = concatcp!("Runs a local web version of ", constants::PRODUCT_NAME_LONG))] ServeWeb(ServeWebArgs), /// Runs the control server on process stdin/stdout From c69c9082378f6b18bb237f8cc04da64f62e5370f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 10:04:34 -0700 Subject: [PATCH 368/607] Fix error in zsh si script Fixes #188875 --- .../contrib/terminal/browser/media/shellIntegration-rc.zsh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh index 09718cfbab2..cc2cb83e0d2 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-rc.zsh @@ -51,7 +51,7 @@ if [ -n "${VSCODE_ENV_PREPEND:-}" ]; then IFS=':' read -rA ADDR <<< "$VSCODE_ENV_PREPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo ${ITEM%%=*})" - export $VARNAME="$(echo -e {ITEM#*=})${(P)VARNAME}" + export $VARNAME="$(echo -e ${ITEM#*=})${(P)VARNAME}" done unset VSCODE_ENV_PREPEND fi @@ -59,7 +59,7 @@ if [ -n "${VSCODE_ENV_APPEND:-}" ]; then IFS=':' read -rA ADDR <<< "$VSCODE_ENV_APPEND" for ITEM in "${ADDR[@]}"; do VARNAME="$(echo ${ITEM%%=*})" - export $VARNAME="${(P)VARNAME}$(echo -e {ITEM#*=})" + export $VARNAME="${(P)VARNAME}$(echo -e ${ITEM#*=})" done unset VSCODE_ENV_APPEND fi From 146a7d807040153ed6ecdc937185db651393b229 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Wed, 30 Aug 2023 10:14:14 -0700 Subject: [PATCH 369/607] Update src/vs/workbench/contrib/terminal/browser/terminalInstance.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- src/vs/workbench/contrib/terminal/browser/terminalInstance.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 64bd477b0e8..645bcb88540 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -976,6 +976,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { return false; } + // Prevent default when shift+tab is being sent to the terminal to avoid it bubbling up + // and changing focus https://github.com/microsoft/vscode/issues/188329 if (event.key === 'Tab' && event.shiftKey) { event.preventDefault(); return true; From 3ff7e76b48dcc4a2ec5421239175432d1d47f2d3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 30 Aug 2023 10:19:08 -0700 Subject: [PATCH 370/607] on blur, hide --- .../accessibility/browser/terminalAccessibleWidget.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts index 5190803c2d0..448cd3da0e4 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleWidget.ts @@ -124,6 +124,7 @@ export abstract class TerminalAccessibleWidget extends DisposableStore { this._terminalService.setActiveInstance(this._instance as ITerminalInstance); this._xtermElement.classList.add(ClassName.Hide); })); + this.add(this._editorWidget.onDidBlurEditorText(async () => this.hide())); } registerListeners(): void { From 8a69a8f266d812d093be0e9c2fd8d41c397e6f75 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 30 Aug 2023 10:41:17 -0700 Subject: [PATCH 371/607] debug: bump js-debug to 1.82 (#191827) --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index 2ae3ee8f0f8..acb24971c39 100644 --- a/product.json +++ b/product.json @@ -52,8 +52,8 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.81.0", - "sha256": "6d1c7ee89881afd65e8fee47445b6a1c5fb345bf30e2bdf70cd2fdd8d1ff6dec", + "version": "1.82.0", + "sha256": "4fba41b4b764c3f5a6591d6d9a5bdc59b417f2d799071c889c2b54163f256282", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", From 6e058e590332c2f85a2097a642a1efc10d2a2fec Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 30 Aug 2023 10:46:39 -0700 Subject: [PATCH 372/607] allow more output mime types to be copied --- .../notebook/browser/contrib/clipboard/cellOutputClipboard.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts index 576ded3d1f6..a544461cc28 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts @@ -61,5 +61,7 @@ export const TEXT_BASED_MIMETYPES = [ 'application/x.notebook.stream', 'application/vnd.code.notebook.stderr', 'application/x.notebook.stderr', - 'text/plain' + 'text/plain', + 'text/markdown', + 'application/json' ]; From faa42cbdd2276d3815ae3485e64067ba28b791da Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 30 Aug 2023 10:52:35 -0700 Subject: [PATCH 373/607] Fix file tree not being transferred to panel chat (#191826) --- src/vs/workbench/contrib/chat/common/chatModel.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 23fec163a79..5f51abedb9a 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -160,6 +160,7 @@ export class Response implements IResponse { }); } else if (isCompleteInteractiveProgressTreeData(responsePart)) { this._responseParts.push(responsePart); + this._updateRepr(quiet); } } From da5492061c96005ca7592623f523c3323983fb09 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 30 Aug 2023 11:13:13 -0700 Subject: [PATCH 374/607] Fix rendering when chat is hidden (#191830) Fixes https://github.com/microsoft/vscode/issues/191704 --- .../contrib/chat/browser/chatQuick.ts | 11 +++++++++++ .../contrib/chat/browser/chatWidget.ts | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 5545e1a5d03..cc32d7adbd3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -92,10 +92,13 @@ export class QuickChatService extends Disposable implements IQuickChatService { // show needs to come after the quickpick is shown this._currentChat.render(this._container); + } else { + this._currentChat.show(); } disposableStore.add(this._input.onDidHide(() => { disposableStore.dispose(); + this._currentChat!.hide(); this._input = undefined; this._onDidClose.fire(); })); @@ -163,6 +166,14 @@ class QuickChat extends Disposable { } } + hide(): void { + this.widget.setVisible(false); + } + + show(): void { + this.widget.setVisible(true); + } + render(parent: HTMLElement): void { if (this.widget) { throw new Error('Cannot render quick chat twice'); diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 7e65ed72d20..b37163a9990 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -213,7 +213,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this._onDidClear.fire(); } - private onDidChangeItems() { + private onDidChangeItems(skipDynamicLayout?: boolean) { if (this.tree && this.visible) { const treeItems = (this.viewModel?.getItems() ?? []) .map(item => { @@ -239,7 +239,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } }); - if (this._dynamicMessageLayoutData) { + if (!skipDynamicLayout && this._dynamicMessageLayoutData) { this.layoutDynamicChatTreeItemMode(); } @@ -270,7 +270,7 @@ export class ChatWidget extends Disposable implements IChatWidget { // Progressive rendering paused while hidden, so start it up again. // Do it after a timeout because the container is not visible yet (it should be but offsetHeight returns 0 here) if (this.visible) { - this.onDidChangeItems(); + this.onDidChangeItems(true); } }, 0)); } @@ -540,6 +540,11 @@ export class ChatWidget extends Disposable implements IChatWidget { const mutableDisposable = this._register(new MutableDisposable()); this._register(this.tree.onDidScroll((e) => { + // TODO@TylerLeonhardt this should probably just be disposed when this is disabled + // and then set up again when it is enabled again + if (!this._dynamicMessageLayoutData?.enabled) { + return; + } mutableDisposable.value = dom.scheduleAtNextAnimationFrame(() => { if (!e.scrollTopChanged || e.heightChanged || e.scrollHeightChanged) { return; @@ -593,7 +598,9 @@ export class ChatWidget extends Disposable implements IChatWidget { if (!this.viewModel || !this._dynamicMessageLayoutData?.enabled) { return; } - const inputHeight = this.inputPart.layout(this._dynamicMessageLayoutData!.maxHeight, this.container.offsetWidth); + + const width = this.bodyDimension?.width ?? this.container.offsetWidth; + const inputHeight = this.inputPart.layout(this._dynamicMessageLayoutData!.maxHeight, width); const totalMessages = this.viewModel.getItems(); // grab the last N messages @@ -610,10 +617,10 @@ export class ChatWidget extends Disposable implements IChatWidget { inputHeight + listHeight + (totalMessages.length > 2 ? 18 : 0), this._dynamicMessageLayoutData!.maxHeight ), - this.container.offsetWidth + width ); - if (needsRerender) { + if (needsRerender || !listHeight) { // TODO: figure out a better place to reveal the last element revealLastElement(this.tree); } From 8470c9b0e32bc4da53d74c89ad3909da18ff1991 Mon Sep 17 00:00:00 2001 From: Andrea Mah <31675041+andreamah@users.noreply.github.com> Date: Wed, 30 Aug 2023 11:17:10 -0700 Subject: [PATCH 375/607] adjust endgame query (#191806) --- .vscode/notebooks/my-endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 96c780e5a6f..6978af0b6f0 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -177,7 +177,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue label:bug label:verification-steps-needed" + "value": "$REPOS $MILESTONE -$MINE is:issue label:bug label:verification-steps-needed -label:verified" }, { "kind": 1, From 272fdf6abf6d3ddedc4197b21585fa615ef313c0 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 16:19:30 +0200 Subject: [PATCH 376/607] Diff Editor: Disables optimistic diff updates. Fixes #190748, Fixes #190232 --- .../diffEditorWidget2/diffEditorViewModel.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index aff79983e40..91839a8d461 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -4,22 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import { RunOnceScheduler } from 'vs/base/common/async'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; -import { isDefined } from 'vs/base/common/types'; +import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; import { Range } from 'vs/editor/common/core/range'; -import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; -import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; import { AdvancedLinesDiffComputer, lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; +import { LineRangeMapping, MovedText, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper'; import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos'; import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; import { DiffEditorOptions } from './diffEditorOptions'; -import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; export class DiffEditorViewModel extends Disposable implements IDiffEditorViewModel { private readonly _isDiffUpToDate = observableValue('isDiffUpToDate', false); @@ -469,6 +468,9 @@ export class UnchangedRegion { } function applyOriginalEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): IDocumentDiff | undefined { + return undefined; + /* + TODO@hediet if (textEdits.length === 0) { return diff; } @@ -478,7 +480,7 @@ function applyOriginalEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig if (!diff3) { return undefined; } - return flip(diff3); + return flip(diff3);*/ } function flip(diff: IDocumentDiff): IDocumentDiff { @@ -491,6 +493,9 @@ function flip(diff: IDocumentDiff): IDocumentDiff { } function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): IDocumentDiff | undefined { + return undefined; + /* + TODO@hediet if (textEdits.length === 0) { return diff; } @@ -514,7 +519,7 @@ function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig quitEarly: false, changes, moves, - }; + };*/ } function applyEditToLineRange(range: LineRange, textEdits: TextEditInfo[]): LineRange | undefined { From 425413a5092365a790cdd0c7006a687513e4572f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 16:45:22 +0200 Subject: [PATCH 377/607] Uncomment unused symbols. --- .../widget/diffEditorWidget2/diffEditorViewModel.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 91839a8d461..729b3d61260 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -482,7 +482,7 @@ function applyOriginalEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig } return flip(diff3);*/ } - +/* function flip(diff: IDocumentDiff): IDocumentDiff { return { changes: diff.changes.map(c => c.flip()), @@ -491,7 +491,7 @@ function flip(diff: IDocumentDiff): IDocumentDiff { quitEarly: diff.quitEarly, }; } - +*/ function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], originalTextModel: ITextModel, modifiedTextModel: ITextModel): IDocumentDiff | undefined { return undefined; /* @@ -521,7 +521,7 @@ function applyModifiedEdits(diff: IDocumentDiff, textEdits: TextEditInfo[], orig moves, };*/ } - +/* function applyEditToLineRange(range: LineRange, textEdits: TextEditInfo[]): LineRange | undefined { let rangeStartLineNumber = range.startLineNumber; let rangeEndLineNumberEx = range.endLineNumberExclusive; @@ -588,3 +588,4 @@ function applyModifiedEditsToLineRangeMappings(changes: readonly LineRangeMappin ); return newChanges; } +*/ From 8e242d5bd40435dee74cfb003af4e931767f55bd Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 30 Aug 2023 20:03:24 +0200 Subject: [PATCH 378/607] Fixes CI --- .../browser/widget/diffEditorWidget2/diffEditorViewModel.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 729b3d61260..860afa96659 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -9,15 +9,13 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; -import { Range } from 'vs/editor/common/core/range'; -import { AdvancedLinesDiffComputer, lineRangeMappingFromRangeMappings } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; -import { LineRangeMapping, MovedText, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { LineRangeMapping, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper'; import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos'; -import { lengthAdd, lengthDiffNonNegative, lengthGetLineCount, lengthOfRange, lengthToPosition, lengthZero, positionToLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; import { DiffEditorOptions } from './diffEditorOptions'; export class DiffEditorViewModel extends Disposable implements IDiffEditorViewModel { From 078a686c8254dd37ff652ea87df7e0ed53e95615 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 30 Aug 2023 20:47:31 +0200 Subject: [PATCH 379/607] Fix screen reader for comment editor (#191828) * Try forwarding the accessibility support setting to the comment editor * Add isAccessible to comment view Fixes #146994 --- .../contrib/comments/browser/commentThreadZoneWidget.ts | 2 +- .../workbench/contrib/comments/browser/simpleCommentEditor.ts | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 1f00b285fa2..74590df5c4c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -129,7 +129,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService ) { - super(editor, { keepEditorSelection: true }); + super(editor, { keepEditorSelection: true, isAccessible: true }); this._contextKeyService = contextKeyService.createScoped(this.domNode); this._scopedInstantiationService = instantiationService.createChild(new ServiceCollection( diff --git a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts index c8ec74a476f..f087642f26b 100644 --- a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts +++ b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts @@ -104,7 +104,8 @@ export class SimpleCommentEditor extends CodeEditorWidget { enabled: false }, autoClosingBrackets: configurationService.getValue('editor.autoClosingBrackets'), - quickSuggestions: false + quickSuggestions: false, + accessibilitySupport: configurationService.getValue<'auto' | 'off' | 'on'>('editor.accessibilitySupport'), }; } } From 0d71f3559452de1026dc6475b5a94ceb0f42d300 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Wed, 30 Aug 2023 11:59:17 -0700 Subject: [PATCH 380/607] Update codicons (#191835) --- .../browser/ui/codicons/codicon/codicon.ttf | Bin 73436 -> 73624 bytes src/vs/base/common/codicons.ts | 1 + 2 files changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index b5623b0a55c7200b6be0079458f48accd5e9eb17..91105610d11595b1232b6618c80bedaf568e09f5 100644 GIT binary patch delta 3559 zcmXBW2~bt%6$kM1hbN2d`z8p2sKjX9h{~pbfGkf`1O#Lec&ra`i)aWB1#yj0u6vXi zqnK#YF_MmrC2CS@G-Df^rm5r5)=sU>@TkqSX{yF*&&e76-T(dW`|f?`<9^?Lcj#l& z;VY)S(Y9nF>?WehYHL+%Oi7Iok;`GC;E}rKjTU#0Z99o#hKT-BUte2QQ+(*eQto?? z#mfPQQird3e~`u0Ti3PyJMsAse7zAOr}vsyRado4I=zx8s+-8M*jm-r>TnC2`SW1z zAK6l6tu?+6{V1E@l+FhqwXRyb&bc)17{MixD5!MbV6?|M0{3Nye|_`RzL9A-XGGuw z_zv8Lr<2iV@;COdXXrKdyZFtz^J8yv{E{4h+?&ifn#gpAOpg709dG{UNHn~Cc=zyU z!#C}&cJqj@G2}9*dIo+%XAy-B@WvAQ0Y#LJ6Ue|Km}v*iL@0I8ReDG%SWjyy5Pf7& zCtidr2*7r_M1AyAY{5$E!djG~5%nlT6V|{A3!O(5Rt>bdy218@p3?v589j$1OmKz^ z+~5vh1R@B*2tgReARG}Gi%5(|EGA$gCLso!Eul zcn$ktU_TDvFpl6Tj^Q}o!b!Z1Q#g%xahC4W-{|l34V6$SHPUQqqH?UJ2C`BeEu&bP zK>ww!w39BPm+wOmRnVU?8i`nk3V(VZ3y}p+lpp}nRDd@ql^#(KU7&x$2{AMYe(<1K z6i*p=83(Z!d+<8-BLPVmMHckq9h^ZsUPT9N=)`8cLOx`M7ahQx#yz+2ImvX4Dk*>l z=tKC>Sc;^7kRJ}gf&N5a&~5rX{f@q(KhPbzOGETW`jUQ&HoAgwM!frYuXM^nCYI7C z^q6O!ld;gCQ8i2VI!%g4_dn>?bcX7FUHRy=`dxJJ=g&RT^iPK&|@&N>Be z&U(dDj)of)@d6rdR46i@c?69=!7Zyo24{=HBFohk`4oP06Yn?o{B$@MeYWhOgIDk1gElQCMl1y@G6Oxy6qHfl|(03Q{RQJ_KYm zuPc;s?p0{w+^4XHvsXdJWGG%FG`wHYdCmh0Rh$R;aUvUDL&IK~PP~T59;J9Sk$s%v zbw&1Q#Vd^LF^ZmZj#uF2Z;w?laZXTh=A5YD!Z}I7jWbSxXMp_$BAaaB%Zt@ z76{^;qU1SepQ;eT$x}oijB}d87|!Vm;ha3B1R^+RDvafvr4UIrvd43SK}PkGf{bdm zf{bd8f{bdef{ZFbK}N;TSpgo9JxM_Z#XkxHGLRGn8OS`rQwUQ1d@J z!7d;LFHn$zGZduYOa&=8OF;@=s2~MoD@Xyh9NsWU0l5lNK%RmWU{;VX%~z0%ELM!<=mjM>u6f3`aR-L=49`Ur{*D*{<*w=c@`QIXe{I=9Gal zoZ{?MI8792-^>k$cR9BxoHZ_oc44)V8rH>cBiF+cY@_*;J_TN6_BRz|Y$p`_Ii&1O{0XQUZe~Czl{l!r8A7zvk!WO&2q9g{iDiCgCt z|JsuM14WZKFDUqN4k&mSqr<1l-`xIlg@c?QDeUFEq_Btc7m9ze$v&t^iu_n1f%CFL z66Y1gzwBhcs>s6miGrN_DUn4s-~rixsUV}hrXV--R|;}7uPeyS{7gY^<_(2T&YMc! zN9~_0ykbN|M2C2B>oOjYEd5f*Q9i!5x#?hvN-vh1Ts0j6~B1Uh>zk|5E}7S^mt%h)PG=mkl>w=o6wzb zE#XmOTw-xzZ{qpH?~=wOolF`^E=)d=(w#Dx@^D_kytaAQ=TDs9IsbZUXlifjgS6Rc z=CqEqYiUo@{nHcE%hCtazg@6t!JUk`8O<4k8Q)}@GdnU*Wj@Uc&Wg>tw=iR2g`Y=27jK+N|2j+6xww<)mfEGGcjJ=Uz9auDx!s?rD8S{qFjw z4Y3XD8lE&BY`oqS+?3ta(=^l^(cIX)v-!T&WKFfUSZ`P#w47ZPx@xd>W@}sPhXc1_ z;?~>fJAUnVrx1R@CBJb_;qVWf9-oAnu|b{~7t1@z2n!2cti{7jB6p*|j-zMdXFp)QUlKexcJh_FC}L~}_i Zql3eH7oUtqn!Cx-+E9HlDG0}%{}0c4tqK4D delta 3355 zcmXZe3s4p36$kM12SijrPy|I03}}#;2tJ4k0)hhamY2K*fySR)<)5)aHG;N)GsqHweV=&d8ljAVI{qNpgzWvVb_w8=? zHHYti_Qr@D2de8zODZocV_D~~t~4JW3&Ef< z7Z>0@au=?S#z6-+=3zWTm(lGsefhm#jwbuNWdFMYrD0> z`jz#TZIUfz%-y)}w6b&w7Ska7ume-EfyR+d2{?syNI^2~qh**yyXZPSq)0T7g;6%?s*h`;LFAZTgN~i@5$io&?ARm?3ifWY8Fp5z-vUAcDSbn0X^fUdBp1}_G zaKuDR#uT_?20Y<~nefJJ%z+Q)!WRLUj|F%cfmnzjyn9AQM^Gh#VB45JlL8%_z-78B8#v3N@%h6W+ugw4fF5q7CiX zhy6H!4s@am29Dr3y74|fzzOu>L!8DL^kV=Y(U0^u`UgFrJj$m^T0vD*gnFZR$_&e4 zzV2SMiT;8~2*Wl^r;ibjIJh7e9(ai|(L>SngxcsL{R(&a|9DDHhvt6o>F0 z4$?WSLO2}AgtIu0b9fs&@eX#uf@Zu$?vw&oI*MK@qc7+i`aRvDWJ;pM1>`|r(pfrB z8MGOz=>!$i3>u+N;YPmXNB^YhI0hKr&a)@HLDw+PSmiw5C5GY=i*))ek2J?KTb(hD z$BHu}4<}jj&x1y@i@(bf9y_H9i;ZCyFN+^B#7$a+y zq6E%rg;Sh03hOv)6;e3s6pu=>))N`Au0EJ$l(~kX(dcva$4O(<)jwnr@7<+%gp$>w z=s9Pzl4)h#t+3aaId!o`w%QbrW3uj5)WX@W(7?H0A&>Kbg4D;8SU|49A%%R-P6es^ zu)`Nay1fr1R?)q%#Q$(iux4FBqgZNea>#o*M$v8J;r&(t$JuX>fyrG?=a+?PVxP zdzlKUoLLIeMz*p*4&JCB2j?ir!MO^upC^id?B@v~P{8>D3VHnn6!H2C*u?8EU^B1t z91tkwERp-qvlL~#Av+mNoU)U_%qjaAsyNFPYB=RM27a2@DioSH<$oC7k3* zP>}w9sUUTXD#&g6or2t^8wzrpzEY6ebW=fY(=7$LO}7<)E|BeO#UBo2yTjKcbgug> zS6=@{k<|4EgsKGYWjeT#oS`tKx5me&6%6WXS!bNGcmw{5?_T_t&K{ zL}T2#AijfRQx#%4-4uUq&=~hd$alv`>%9NMa&^^{u;8%zu>P=n;SS*u;RWG+;Wt;O ztZt96i%5zLjxyV& z>3Y%wOR{rvaB^C5WAfdU$tejb^(p-+KcohxwxwQ9y_YsEt#^adhCLf@r$?nXrC-c& z&PdPrJhL_PbmsM}p{)B^kF)1wo3h)oFK7R}v18-V#s@ipIY~K9IXyXdb4|HDxi|8h z^G5R?=GW#A7I+kd7wjk)D-0~GE*vVf76ljWEc&jf{K=;1O@A(4vUy4AgEEh@6=nTp zH_QH0?p$6}K5R-cwVS$41E$NSn-zf-=8A!eN9Gmg0rR(8{I;B{jIL~`9Ibp@wWO-O z>UOnfb!c^G^>FpW8t Date: Wed, 30 Aug 2023 20:54:49 +0200 Subject: [PATCH 381/607] Fixes #191617 --- .../diffEditorWidget2/movedBlocksLines.ts | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 8a3ac863ddc..8a618685996 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -9,7 +9,7 @@ import { Action } from 'vs/base/common/actions'; import { booleanComparator, compareBy, findMaxIdxBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; import { Codicon } from 'vs/base/common/codicons'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, autorunWithStore, constObservable, derived, derivedWithStore, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable'; +import { IObservable, autorun, autorunHandleChanges, autorunWithStore, constObservable, derived, derivedWithStore, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; @@ -82,19 +82,50 @@ export class MovedBlocksLinesPart extends Disposable { } })); - this._register(this._editors.original.onDidChangeCursorPosition(e => { - const m = this._diffModel.get(); - if (!m) { return; } - const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.original.contains(e.position.lineNumber)); - if (movedText !== m.movedTextToCompare.get()) { - m.movedTextToCompare.set(undefined, undefined); + const originalCursorPosition = observableFromEvent(this._editors.original.onDidChangeCursorPosition, () => this._editors.original.getPosition()); + const modifiedCursorPosition = observableFromEvent(this._editors.modified.onDidChangeCursorPosition, () => this._editors.modified.getPosition()); + const originalHasFocus = observableSignalFromEvent( + 'original.onDidFocusEditorWidget', + e => this._editors.original.onDidFocusEditorWidget(() => setTimeout(() => e(undefined), 0)) + ); + const modifiedHasFocus = observableSignalFromEvent( + 'modified.onDidFocusEditorWidget', + e => this._editors.modified.onDidFocusEditorWidget(() => setTimeout(() => e(undefined), 0)) + ); + + let lastChangedEditor: 'original' | 'modified' = 'modified'; + + this._register(autorunHandleChanges({ + createEmptyChangeSummary: () => undefined, + handleChange: (ctx, summary) => { + if (ctx.didChange(originalHasFocus)) { lastChangedEditor = 'original'; } + if (ctx.didChange(modifiedHasFocus)) { lastChangedEditor = 'modified'; } + return true; } - m.setActiveMovedText(movedText); - })); - this._register(this._editors.modified.onDidChangeCursorPosition(e => { - const m = this._diffModel.get(); + }, reader => { + originalHasFocus.read(reader); + modifiedHasFocus.read(reader); + + const m = this._diffModel.read(reader); if (!m) { return; } - const movedText = m.diff.get()!.movedTexts.find(m => m.lineRangeMapping.modified.contains(e.position.lineNumber)); + const diff = m.diff.read(reader); + + let movedText: MovedText | undefined = undefined; + + if (diff && lastChangedEditor === 'original') { + const originalPos = originalCursorPosition.read(reader); + if (originalPos) { + movedText = diff.movedTexts.find(m => m.lineRangeMapping.original.contains(originalPos.lineNumber)); + } + } + + if (diff && lastChangedEditor === 'modified') { + const modifiedPos = modifiedCursorPosition.read(reader); + if (modifiedPos) { + movedText = diff.movedTexts.find(m => m.lineRangeMapping.modified.contains(modifiedPos.lineNumber)); + } + } + if (movedText !== m.movedTextToCompare.get()) { m.movedTextToCompare.set(undefined, undefined); } From 08631fab3a63e8439bec69dce08aa5cf95360d48 Mon Sep 17 00:00:00 2001 From: songlinn <17741492+songlinn@users.noreply.github.com> Date: Thu, 31 Aug 2023 03:57:58 +0800 Subject: [PATCH 382/607] fix: prevent history show prev/next in composing event (#184014) --- src/vs/platform/history/browser/contextScopedHistoryWidget.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/platform/history/browser/contextScopedHistoryWidget.ts b/src/vs/platform/history/browser/contextScopedHistoryWidget.ts index 0278e0c0112..53b9397651d 100644 --- a/src/vs/platform/history/browser/contextScopedHistoryWidget.ts +++ b/src/vs/platform/history/browser/contextScopedHistoryWidget.ts @@ -113,6 +113,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and( ContextKeyExpr.has(HistoryNavigationWidgetFocusContext), ContextKeyExpr.equals(HistoryNavigationBackwardsEnablementContext, true), + ContextKeyExpr.not('isComposing'), historyNavigationVisible.isEqualTo(false), ), primary: KeyCode.UpArrow, @@ -128,6 +129,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ when: ContextKeyExpr.and( ContextKeyExpr.has(HistoryNavigationWidgetFocusContext), ContextKeyExpr.equals(HistoryNavigationForwardsEnablementContext, true), + ContextKeyExpr.not('isComposing'), historyNavigationVisible.isEqualTo(false), ), primary: KeyCode.DownArrow, From e5851bc5f53b2229d34402fb6c816e82124b00a7 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 30 Aug 2023 14:34:42 -0700 Subject: [PATCH 383/607] look for re-used output id containing the image --- .../browser/controller/cellOutputActions.ts | 2 +- .../contrib/notebook/browser/notebookBrowser.ts | 1 + .../notebook/browser/notebookEditorWidget.ts | 2 +- .../browser/view/renderers/backLayerWebView.ts | 6 ++++-- .../browser/view/renderers/webviewMessages.ts | 2 ++ .../browser/view/renderers/webviewPreloads.ts | 15 ++++++++------- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index 956222d7a36..fa43716b286 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -55,7 +55,7 @@ registerAction2(class CopyCellOutputAction extends Action2 { const mimeType = outputViewModel.pickedMimeType?.mimeType; if (mimeType?.startsWith('image/')) { - const focusOptions = { skipReveal: true, outputId: outputViewModel.model.outputId }; + const focusOptions = { skipReveal: true, outputId: outputViewModel.model.outputId, altOutputId: outputViewModel.model.alternativeOutputId }; await notebookEditor.focusNotebookCell(outputViewModel.cellViewModel as ICellViewModel, 'output', focusOptions); notebookEditor.copyOutputImage(outputViewModel); } else { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 4f1d98b86bb..158b98692fe 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -152,6 +152,7 @@ export interface IFocusNotebookCellOptions { readonly focusEditorLine?: number; readonly minimalScrolling?: boolean; readonly outputId?: string; + readonly altOutputId?: string; } //#endregion diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index ba8ae7da1db..3830664287b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -2350,7 +2350,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } const focusElementId = options?.outputId ?? cell.id; - this._webview.focusOutput(focusElementId, this._webviewFocused); + this._webview.focusOutput(focusElementId, options?.altOutputId, this._webviewFocused); cell.updateEditState(CellEditState.Preview, 'focusNotebookCell'); cell.focusMode = CellFocusMode.Output; 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 b2359655e6d..aaed4af8425 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1548,7 +1548,8 @@ export class BackLayerWebView extends Themable { async copyImage(output: ICellOutputViewModel): Promise { this._sendMessageToWebview({ type: 'copyImage', - outputId: output.model.outputId + outputId: output.model.outputId, + altOutputId: output.model.alternativeOutputId }); } @@ -1608,7 +1609,7 @@ export class BackLayerWebView extends Themable { this.webview?.focus(); } - focusOutput(cellOrOutputId: string, viewFocused: boolean) { + focusOutput(cellOrOutputId: string, backupId: string | undefined, viewFocused: boolean) { if (this._disposed) { return; } @@ -1620,6 +1621,7 @@ export class BackLayerWebView extends Themable { this._sendMessageToWebview({ type: 'focus-output', cellOrOutputId: cellOrOutputId, + backupId: backupId }); } 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 e74e173ae33..f16d781611a 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -269,11 +269,13 @@ export interface IShowOutputMessage { export interface ICopyImageMessage { readonly type: 'copyImage'; readonly outputId: string; + readonly altOutputId: string; } export interface IFocusOutputMessage { readonly type: 'focus-output'; readonly cellOrOutputId: string; + readonly backupId?: string; } export interface IAckOutputHeight { 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 4715e64aa08..cadaa48312f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -474,8 +474,9 @@ async function webviewPreloads(ctx: PreloadContext) { }); }; - function focusFirstFocusableOrContainerInOutput(cellOrOutputId: string) { - const cellOutputContainer = document.getElementById(cellOrOutputId); + function focusFirstFocusableOrContainerInOutput(cellOrOutputId: string, backupId?: string) { + const cellOutputContainer = document.getElementById(cellOrOutputId) ?? + backupId ? document.getElementById(backupId!) : undefined; if (cellOutputContainer) { if (cellOutputContainer.contains(document.activeElement)) { return; @@ -1362,17 +1363,17 @@ async function webviewPreloads(ctx: PreloadContext) { }); }; - const copyOutputImage = async (outputId: string, retries = 5) => { + const copyOutputImage = async (outputId: string, altOutputId: string, retries = 5) => { if (!document.hasFocus() && retries > 0) { // copyImage can be called from outside of the webview, which means this function may be running whilst the webview is gaining focus. // Since navigator.clipboard.write requires the document to be focused, we need to wait for focus. // We cannot use a listener, as there is a high chance the focus is gained during the setup of the listener resulting in us missing it. - setTimeout(() => { copyOutputImage(outputId, retries - 1); }, 20); + setTimeout(() => { copyOutputImage(outputId, altOutputId, retries - 1); }, 20); return; } try { - const image = document.getElementById(outputId)?.querySelector('img'); + const image = document.getElementById(outputId)?.querySelector('img') || document.getElementById(altOutputId)?.querySelector('img'); if (image) { await navigator.clipboard.write([new ClipboardItem({ 'image/png': new Promise((resolve) => { @@ -1501,7 +1502,7 @@ async function webviewPreloads(ctx: PreloadContext) { } case 'copyImage': { - await copyOutputImage(event.data.outputId); + await copyOutputImage(event.data.outputId, event.data.altOutputId); break; } @@ -1524,7 +1525,7 @@ async function webviewPreloads(ctx: PreloadContext) { break; } case 'focus-output': - focusFirstFocusableOrContainerInOutput(event.data.cellOrOutputId); + focusFirstFocusableOrContainerInOutput(event.data.cellOrOutputId, event.data.backupId); break; case 'decorations': { let outputContainer = document.getElementById(event.data.cellId); From 2e4187c15f7fe9b32445f64ccdb800af3bb8f531 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 30 Aug 2023 14:39:22 -0700 Subject: [PATCH 384/607] normalize option name --- .../notebook/browser/view/renderers/backLayerWebView.ts | 4 ++-- .../notebook/browser/view/renderers/webviewMessages.ts | 2 +- .../notebook/browser/view/renderers/webviewPreloads.ts | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) 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 aaed4af8425..b89185074f7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -1609,7 +1609,7 @@ export class BackLayerWebView extends Themable { this.webview?.focus(); } - focusOutput(cellOrOutputId: string, backupId: string | undefined, viewFocused: boolean) { + focusOutput(cellOrOutputId: string, alternateId: string | undefined, viewFocused: boolean) { if (this._disposed) { return; } @@ -1621,7 +1621,7 @@ export class BackLayerWebView extends Themable { this._sendMessageToWebview({ type: 'focus-output', cellOrOutputId: cellOrOutputId, - backupId: backupId + alternateId: alternateId }); } 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 f16d781611a..b46964be307 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -275,7 +275,7 @@ export interface ICopyImageMessage { export interface IFocusOutputMessage { readonly type: 'focus-output'; readonly cellOrOutputId: string; - readonly backupId?: string; + readonly alternateId?: string; } export interface IAckOutputHeight { 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 cadaa48312f..ef41ec0d8b4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -474,9 +474,9 @@ async function webviewPreloads(ctx: PreloadContext) { }); }; - function focusFirstFocusableOrContainerInOutput(cellOrOutputId: string, backupId?: string) { + function focusFirstFocusableOrContainerInOutput(cellOrOutputId: string, alternateId?: string) { const cellOutputContainer = document.getElementById(cellOrOutputId) ?? - backupId ? document.getElementById(backupId!) : undefined; + alternateId ? document.getElementById(alternateId!) : undefined; if (cellOutputContainer) { if (cellOutputContainer.contains(document.activeElement)) { return; @@ -1525,7 +1525,7 @@ async function webviewPreloads(ctx: PreloadContext) { break; } case 'focus-output': - focusFirstFocusableOrContainerInOutput(event.data.cellOrOutputId, event.data.backupId); + focusFirstFocusableOrContainerInOutput(event.data.cellOrOutputId, event.data.alternateId); break; case 'decorations': { let outputContainer = document.getElementById(event.data.cellId); From 9fbe99c616a42350af45dbb72b6c6bcaadad8a21 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 30 Aug 2023 14:41:03 -0700 Subject: [PATCH 385/607] cleanup --- .../notebook/browser/view/renderers/webviewPreloads.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 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 ef41ec0d8b4..5197ae5372d 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -1373,7 +1373,8 @@ async function webviewPreloads(ctx: PreloadContext) { } try { - const image = document.getElementById(outputId)?.querySelector('img') || document.getElementById(altOutputId)?.querySelector('img'); + const image = document.getElementById(outputId)?.querySelector('img') + ?? document.getElementById(altOutputId)?.querySelector('img'); if (image) { await navigator.clipboard.write([new ClipboardItem({ 'image/png': new Promise((resolve) => { @@ -1501,9 +1502,7 @@ async function webviewPreloads(ctx: PreloadContext) { break; } case 'copyImage': { - await copyOutputImage(event.data.outputId, event.data.altOutputId); - break; } case 'ack-dimension': { From 3217686db153a4bf9ff11a74beb5288c102c41e4 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 30 Aug 2023 15:12:33 -0700 Subject: [PATCH 386/607] get `x` off the edge (#191849) Fixes https://github.com/microsoft/vscode/issues/191701 --- src/vs/workbench/contrib/chat/browser/media/chat.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 5c36c297345..eb1c33b9fa6 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -238,7 +238,7 @@ .interactive-session .interactive-input-and-side-toolbar { display: flex; - gap: 6px; + gap: 4px; align-items: center; } @@ -427,6 +427,7 @@ .quick-input-widget .interactive-session .interactive-input-and-execute-toolbar { margin: 0; border-radius: 2px; + padding: 0 4px 0 6px; } .quick-input-widget .interactive-list { From d89cac9ac10d1aa4310e827c458f2bea597685ed Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 30 Aug 2023 15:20:02 -0700 Subject: [PATCH 387/607] Partially revert a change that broke dimming in active group Reopens #191608 --- .../browser/unfocusedViewDimmingContribution.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index d4c7a06284c..0d9c1bec943 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -45,19 +45,19 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor // Terminals rules.add(`.monaco-workbench .pane-body.integrated-terminal .terminal-wrapper:not(:focus-within) { ${filterRule} }`); // Text editors - rules.add(`.monaco-workbench .editor-group-container:not(.active) .monaco-editor { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .monaco-editor { ${filterRule} }`); // Breadcrumbs - rules.add(`.monaco-workbench .editor-group-container:not(.active) .tabs-breadcrumbs { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .tabs-breadcrumbs { ${filterRule} }`); // Terminal editors - rules.add(`.monaco-workbench .editor-group-container:not(.active) .terminal-wrapper { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .terminal-wrapper { ${filterRule} }`); // Settings editor - rules.add(`.monaco-workbench .editor-group-container:not(.active) .settings-editor { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .settings-editor { ${filterRule} }`); // Keybindings editor - rules.add(`.monaco-workbench .editor-group-container:not(.active) .keybindings-editor { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .keybindings-editor { ${filterRule} }`); // Editor placeholder (error case) - rules.add(`.monaco-workbench .editor-group-container:not(.active) .monaco-editor-pane-placeholder { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .monaco-editor-pane-placeholder { ${filterRule} }`); // Welcome editor - rules.add(`.monaco-workbench .editor-group-container:not(.active) .gettingStartedContainer { ${filterRule} }`); + rules.add(`.monaco-workbench .editor-instance:not(:focus-within) .gettingStartedContainer { ${filterRule} }`); cssTextContent = [...rules].join('\n'); } From 8f1af4b86865b421a585e398c53a53726cdd41a6 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Wed, 30 Aug 2023 15:24:03 -0700 Subject: [PATCH 388/607] Show focus state on editor tabs in hc themes (#191850) --- src/vs/workbench/browser/parts/editor/tabsTitleControl.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 3272b3e4839..5d5d132b05d 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -2077,6 +2077,10 @@ registerThemingParticipant((theme, collector) => { outline-offset: -5px; } + .monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:focus { + outline-style: dashed; + } + .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active { outline: 1px dotted; outline-offset: -5px; From 700bf1a4db0c2b9b557c4ffe19b8b57c2a485f05 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 30 Aug 2023 15:29:07 -0700 Subject: [PATCH 389/607] Fix progressive rendering of updated markdown content (#191851) Fix progressive rendering updated markdown content --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 06f2c24dfb6..8ba6d711809 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -452,9 +452,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer Date: Thu, 31 Aug 2023 00:35:53 +0200 Subject: [PATCH 390/607] view descriptor created with containerTitle that is not a string (#191842) --- src/vs/workbench/api/browser/viewsExtensionPoint.ts | 8 ++++---- src/vs/workbench/common/views.ts | 10 ++-------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 5fc7d5dc63d..f97885e8e02 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -18,7 +18,7 @@ import { Extensions as ViewletExtensions, PaneCompositeRegistry } from 'vs/workb import { CustomTreeView, RawCustomTreeViewContextKey, TreeViewPane } from 'vs/workbench/browser/parts/views/treeView'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { Extensions as ViewContainerExtensions, ICustomTreeViewDescriptor, ICustomViewDescriptor, IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ResolvableTreeItem, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; +import { Extensions as ViewContainerExtensions, ICustomViewDescriptor, IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ResolvableTreeItem, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug'; import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files'; import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/browser/remoteExplorer'; @@ -530,14 +530,14 @@ class ViewsExtensionHandler implements IWorkbenchContribution { } } - const viewDescriptor = { + const viewDescriptor: ICustomViewDescriptor = { type: type, ctorDescriptor: type === ViewType.Tree ? new SyncDescriptor(TreeViewPane) : new SyncDescriptor(WebviewViewPane), id: item.id, name: item.name, when: ContextKeyExpr.deserialize(item.when), containerIcon: icon || viewContainer?.icon, - containerTitle: item.contextualTitle || viewContainer?.title, + containerTitle: item.contextualTitle || (viewContainer && (typeof viewContainer.title === 'string' ? viewContainer.title : viewContainer.title.value)), canToggleVisibility: true, canMoveView: viewContainer?.id !== REMOTE, treeView: type === ViewType.Tree ? this.instantiationService.createInstance(CustomTreeView, item.id, item.name, extension.description.identifier.value) : undefined, @@ -587,7 +587,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution { if (removedViews.length) { this.viewsRegistry.deregisterViews(removedViews, viewContainer); for (const view of removedViews) { - const anyView = view as ICustomTreeViewDescriptor; + const anyView = view as ICustomViewDescriptor; if (anyView.treeView) { anyView.treeView.dispose(); } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 95f89542805..07578cb75b6 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -300,18 +300,12 @@ export interface IViewDescriptor { readonly openCommandActionDescriptor?: OpenCommandActionDescriptor; } -export interface ICustomTreeViewDescriptor extends ITreeViewDescriptor { +export interface ICustomViewDescriptor extends IViewDescriptor { readonly extensionId: ExtensionIdentifier; readonly originalContainerId: string; + readonly treeView?: ITreeView; } -export interface ICustomWebviewViewDescriptor extends IViewDescriptor { - readonly extensionId: ExtensionIdentifier; - readonly originalContainerId: string; -} - -export type ICustomViewDescriptor = ICustomTreeViewDescriptor | ICustomWebviewViewDescriptor; - export interface IViewDescriptorRef { viewDescriptor: IViewDescriptor; index: number; From e7756c8870ee1df7360e6624e220534174039b02 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 30 Aug 2023 17:19:18 -0700 Subject: [PATCH 391/607] reinstate `github-auth` parameter that was accidentally removed (#191862) Fixes https://github.com/microsoft/vscode/issues/191861 --- src/vs/code/browser/workbench/workbench.ts | 42 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 029cf8b10e7..962e2f4c314 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -17,6 +17,7 @@ import product from 'vs/platform/product/common/product'; import { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/window/common/window'; import type { IWorkbenchConstructionOptions } from 'vs/workbench/browser/web.api'; +import { AuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import type { IWorkspace, IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; import type { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; import { create } from 'vs/workbench/workbench.web.main'; @@ -176,11 +177,13 @@ export class LocalStorageSecretStorageProvider implements ISecretStorageProvider ) { } private async load(): Promise> { + const record = this.loadAuthSessionFromElement(); // Get the secrets from localStorage const encrypted = window.localStorage.getItem(this._storageKey); if (encrypted) { try { - return JSON.parse(await this.crypto.unseal(encrypted)); + const decrypted = JSON.parse(await this.crypto.unseal(encrypted)); + return { ...record, ...decrypted }; } catch (err) { // TODO: send telemetry console.error('Failed to decrypt secrets from localStorage', err); @@ -188,7 +191,42 @@ export class LocalStorageSecretStorageProvider implements ISecretStorageProvider } } - return {}; + return record; + } + + private loadAuthSessionFromElement(): Record { + let authSessionInfo: (AuthenticationSessionInfo & { scopes: string[][] }) | undefined; + const authSessionElement = document.getElementById('vscode-workbench-auth-session'); + const authSessionElementAttribute = authSessionElement ? authSessionElement.getAttribute('data-settings') : undefined; + if (authSessionElementAttribute) { + try { + authSessionInfo = JSON.parse(authSessionElementAttribute); + } catch (error) { /* Invalid session is passed. Ignore. */ } + } + + if (!authSessionInfo) { + return {}; + } + + const record: Record = {}; + + // Settings Sync Entry + record[`${product.urlProtocol}.loginAccount`] = JSON.stringify(authSessionInfo); + + // Auth extension Entry + if (authSessionInfo.providerId !== 'github') { + console.error(`Unexpected auth provider: ${authSessionInfo.providerId}. Expected 'github'.`); + return record; + } + + const authAccount = JSON.stringify({ extensionId: 'vscode.github-authentication', key: 'github.auth' }); + record[authAccount] = JSON.stringify(authSessionInfo.scopes.map(scopes => ({ + id: authSessionInfo!.id, + scopes, + accessToken: authSessionInfo!.accessToken + }))); + + return record; } async get(key: string): Promise { From 9e927a6111882ef550806e06efb82be9b1b55571 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 30 Aug 2023 22:24:20 -0700 Subject: [PATCH 392/607] After 30s re-layout the quick chat (#191853) fixes https://github.com/microsoft/vscode/issues/191627 --- .../workbench/contrib/chat/browser/chatQuick.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index cc32d7adbd3..32592d223ab 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -5,9 +5,10 @@ import * as dom from 'vs/base/browser/dom'; import { Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; +import { disposableTimeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -132,6 +133,7 @@ class QuickChat extends Disposable { private sash!: Sash; private model: ChatModel | undefined; private _currentQuery: string | undefined; + private maintainScrollTimer: MutableDisposable = this._register(new MutableDisposable()); constructor( private readonly _options: IChatViewOptions, @@ -168,10 +170,22 @@ class QuickChat extends Disposable { hide(): void { this.widget.setVisible(false); + // Maintain scroll position for a short time so that if the user re-shows the chat + // the same scroll position will be used. + this.maintainScrollTimer.value = disposableTimeout(() => { + // At this point, clear this mutable disposable which will be our signal that + // the timer has expired and we should stop maintaining scroll position + this.maintainScrollTimer.clear(); + }, 30 * 1000); // 30 seconds } show(): void { this.widget.setVisible(true); + // If the mutable disposable is set, then we are keeping the existing scroll position + // so we should not update the layout. + if (!this.maintainScrollTimer.value) { + this.widget.layoutDynamicChatTreeItemMode(); + } } render(parent: HTMLElement): void { From 9293e05e89108972c3c70a9326bd454b2a2ea8d3 Mon Sep 17 00:00:00 2001 From: Karel Frederix Date: Thu, 31 Aug 2023 09:38:24 +0200 Subject: [PATCH 393/607] wrap handler for resize observer in requestAnimationFrame() (#183325) * wrap handler for resize observer in requestAnimationFrame() (fixes #183324) * React immediately on first notification during an animation frame and only delay the second notification during the same animation frame --------- Co-authored-by: Karel Frederix Co-authored-by: Alexandru Dima --- .../browser/config/elementSizeObserver.ts | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index 3529822e209..f6b344f1872 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -41,12 +41,42 @@ export class ElementSizeObserver extends Disposable { public startObserving(): void { if (!this._resizeObserver && this._referenceDomElement) { - this._resizeObserver = new ResizeObserver((entries) => { - if (entries && entries[0] && entries[0].contentRect) { - this.observe({ width: entries[0].contentRect.width, height: entries[0].contentRect.height }); + // We want to react to the resize observer only once per animation frame + // The first time the resize observer fires, we will react to it immediately. + // Otherwise we will postpone to the next animation frame. + // We'll use `observeContentRect` to store the content rect we received. + + let observeContentRect: DOMRectReadOnly | null = null; + const observeNow = () => { + if (observeContentRect) { + this.observe({ width: observeContentRect.width, height: observeContentRect.height }); } else { this.observe(); } + }; + + let shouldObserve = false; + let alreadyObservedThisAnimationFrame = false; + + const update = () => { + if (shouldObserve && !alreadyObservedThisAnimationFrame) { + try { + shouldObserve = false; + alreadyObservedThisAnimationFrame = true; + observeNow(); + } finally { + requestAnimationFrame(() => { + alreadyObservedThisAnimationFrame = false; + update(); + }); + } + } + }; + + this._resizeObserver = new ResizeObserver((entries) => { + observeContentRect = (entries && entries[0] && entries[0].contentRect ? entries[0].contentRect : null); + shouldObserve = true; + update(); }); this._resizeObserver.observe(this._referenceDomElement); } From 6d62f83a762fc9b82e663a2adde684977daade97 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 31 Aug 2023 10:01:25 +0200 Subject: [PATCH 394/607] voice - start to better understand different chat input contexts (#191884) * voice - start to have context and actions per chat kind * voice - add context to `stop` * voice - add todo for focus issue when starting --- .../actions/voiceChatActions.ts | 310 ++++++++++++++---- 1 file changed, 247 insertions(+), 63 deletions(-) diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 05e454d36a2..b244992f104 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -17,7 +17,7 @@ import { ContextKeyExpr, IContextKeyService, RawContextKey } from 'vs/platform/c import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { CHAT_CATEGORY } from 'vs/workbench/contrib/chat/browser/actions/chatActions'; -import { IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatWidget, IChatWidgetService, IQuickChatService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IWorkbenchVoiceRecognitionService } from 'vs/workbench/services/voiceRecognition/electron-sandbox/workbenchVoiceRecognitionService'; import { MENU_INLINE_CHAT_WIDGET } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -34,15 +34,25 @@ import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatC import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { KeyCode } from 'vs/base/common/keyCodes'; import { isExecuteActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; +import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; -const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone.") }); -const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress.") }); +const CONTEXT_VOICE_CHAT_GETTING_READY = new RawContextKey('voiceChatGettingReady', false, { type: 'boolean', description: localize('voiceChatGettingReady', "True when getting ready for receiving voice input from the microphone for voice chat.") }); +const CONTEXT_VOICE_CHAT_IN_PROGRESS = new RawContextKey('voiceChatInProgress', false, { type: 'boolean', description: localize('voiceChatInProgress', "True when voice recording from microphone is in progress for voice chat.") }); + +const CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS = new RawContextKey('quickVoiceChatInProgress', false, { type: 'boolean', description: localize('quickVoiceChatInProgress', "True when voice recording from microphone is in progress for quick chat.") }); +const CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS = new RawContextKey('inlineVoiceChatInProgress', false, { type: 'boolean', description: localize('inlineVoiceChatInProgress', "True when voice recording from microphone is in progress for inline chat.") }); +const CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS = new RawContextKey('voiceChatInViewInProgress', false, { type: 'boolean', description: localize('voiceChatInViewInProgress', "True when voice recording from microphone is in progress in the chat view.") }); +const CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS = new RawContextKey('voiceChatInEditorInProgress', false, { type: 'boolean', description: localize('voiceChatInEditorInProgress', "True when voice recording from microphone is in progress in the chat editor.") }); + +type VoiceChatSessionContext = 'inline' | 'quick' | 'view' | 'editor'; interface IVoiceChatSessionController { readonly onDidAcceptInput: Event; readonly onDidCancelInput: Event; + readonly context: VoiceChatSessionContext; + focusInput(): void; acceptInput(): void; updateInput(text: string): void; @@ -61,6 +71,7 @@ class VoiceChatSessionControllerFactory { const chatContributionService = accessor.get(IChatContributionService); const editorService = accessor.get(IEditorService); const quickChatService = accessor.get(IQuickChatService); + const layoutService = accessor.get(IWorkbenchLayoutService); // Currently Focussed Context if (context === 'focussed') { @@ -70,19 +81,21 @@ class VoiceChatSessionControllerFactory { // https://github.com/microsoft/vscode/issues/191191 const chatInput = chatWidgetService.lastFocusedWidget; if (chatInput?.hasInputFocus()) { - return { - onDidAcceptInput: chatInput.onDidAcceptInput, - onDidCancelInput: Event.any( - // Since we do not know if the view or the quick chat - // is container of the chat input, we need to listen - // to both events here... - Event.filter(viewsService.onDidChangeViewVisibility, e => e.id === chatContributionService.getViewIdForProvider(chatInput.providerId)), - quickChatService.onDidClose - ), - focusInput: () => chatInput.focusInput(), - acceptInput: () => chatInput.acceptInput(), - updateInput: text => chatInput.updateInput(text) - }; + // Unfortunately there does not seem to be a better way + // to figure out if the chat widget is in a part or picker + if ( + layoutService.hasFocus(Parts.SIDEBAR_PART) || + layoutService.hasFocus(Parts.PANEL_PART) || + layoutService.hasFocus(Parts.AUXILIARYBAR_PART) + ) { + return VoiceChatSessionControllerFactory.doCreateForChatView(chatInput, viewsService, chatContributionService); + } + + if (layoutService.hasFocus(Parts.EDITOR_PART)) { + return VoiceChatSessionControllerFactory.doCreateForChatEditor(chatInput, viewsService, chatContributionService); + } + + return VoiceChatSessionControllerFactory.doCreateForQuickChat(chatInput, quickChatService); } // Try with the inline chat @@ -90,13 +103,7 @@ class VoiceChatSessionControllerFactory { if (activeCodeEditor) { const inlineChat = InlineChatController.get(activeCodeEditor); if (inlineChat?.hasFocus()) { - return { - onDidAcceptInput: inlineChat.onDidAcceptInput, - onDidCancelInput: inlineChat.onDidCancelInput, - focusInput: () => inlineChat.focus(), - acceptInput: () => inlineChat.acceptInput(), - updateInput: text => inlineChat.updateInput(text) - }; + return VoiceChatSessionControllerFactory.doCreateForInlineChat(inlineChat); } } } @@ -107,13 +114,7 @@ class VoiceChatSessionControllerFactory { if (provider) { const chatView = await chatWidgetService.revealViewForProvider(provider.id); if (chatView) { - return { - onDidAcceptInput: chatView.onDidAcceptInput, - onDidCancelInput: Event.filter(viewsService.onDidChangeViewVisibility, e => e.id === chatContributionService.getViewIdForProvider(provider.id)), - focusInput: () => chatView.focusInput(), - acceptInput: () => chatView.acceptInput(), - updateInput: text => chatView.updateInput(text) - }; + return VoiceChatSessionControllerFactory.doCreateForChatView(chatView, viewsService, chatContributionService); } } } @@ -124,18 +125,7 @@ class VoiceChatSessionControllerFactory { if (activeCodeEditor) { const inlineChat = InlineChatController.get(activeCodeEditor); if (inlineChat) { - const inlineChatSession = inlineChat.run(); - - return { - onDidAcceptInput: inlineChat.onDidAcceptInput, - onDidCancelInput: Event.any( - inlineChat.onDidCancelInput, - Event.fromPromise(inlineChatSession) - ), - focusInput: () => inlineChat.focus(), - acceptInput: () => inlineChat.acceptInput(), - updateInput: text => inlineChat.updateInput(text) - }; + return VoiceChatSessionControllerFactory.doCreateForInlineChat(inlineChat); } } } @@ -146,18 +136,59 @@ class VoiceChatSessionControllerFactory { const quickChat = chatWidgetService.lastFocusedWidget; if (quickChat) { - return { - onDidAcceptInput: quickChat.onDidAcceptInput, - onDidCancelInput: quickChatService.onDidClose, - focusInput: () => quickChat.focusInput(), - acceptInput: () => quickChat.acceptInput(), - updateInput: text => quickChat.updateInput(text) - }; + return VoiceChatSessionControllerFactory.doCreateForQuickChat(quickChat, quickChatService); } } return undefined; } + + private static doCreateForChatView(chatView: IChatWidget, viewsService: IViewsService, chatContributionService: IChatContributionService): IVoiceChatSessionController { + return VoiceChatSessionControllerFactory.doCreateForChatViewOrEditor('view', chatView, viewsService, chatContributionService); + } + + private static doCreateForChatEditor(chatView: IChatWidget, viewsService: IViewsService, chatContributionService: IChatContributionService): IVoiceChatSessionController { + return VoiceChatSessionControllerFactory.doCreateForChatViewOrEditor('editor', chatView, viewsService, chatContributionService); + } + + private static doCreateForChatViewOrEditor(context: 'view' | 'editor', chatView: IChatWidget, viewsService: IViewsService, chatContributionService: IChatContributionService): IVoiceChatSessionController { + return { + context, + onDidAcceptInput: chatView.onDidAcceptInput, + // TODO@bpasero cancellation needs to work better for chat editors that are not view bound + onDidCancelInput: Event.filter(viewsService.onDidChangeViewVisibility, e => e.id === chatContributionService.getViewIdForProvider(chatView.providerId)), + focusInput: () => chatView.focusInput(), + acceptInput: () => chatView.acceptInput(), + updateInput: text => chatView.updateInput(text) + }; + } + + private static doCreateForQuickChat(quickChat: IChatWidget, quickChatService: IQuickChatService): IVoiceChatSessionController { + return { + context: 'quick', + onDidAcceptInput: quickChat.onDidAcceptInput, + onDidCancelInput: quickChatService.onDidClose, + focusInput: () => quickChat.focusInput(), + acceptInput: () => quickChat.acceptInput(), + updateInput: text => quickChat.updateInput(text) + }; + } + + private static doCreateForInlineChat(inlineChat: InlineChatController,): IVoiceChatSessionController { + const inlineChatSession = inlineChat.run(); + + return { + context: 'inline', + onDidAcceptInput: inlineChat.onDidAcceptInput, + onDidCancelInput: Event.any( + inlineChat.onDidCancelInput, + Event.fromPromise(inlineChatSession) + ), + focusInput: () => inlineChat.focus(), + acceptInput: () => inlineChat.acceptInput(), + updateInput: text => inlineChat.updateInput(text) + }; + } } interface ActiveVoiceChatSession { @@ -179,6 +210,11 @@ class VoiceChatSessions { private voiceChatInProgressKey = CONTEXT_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); private voiceChatGettingReadyKey = CONTEXT_VOICE_CHAT_GETTING_READY.bindTo(this.contextKeyService); + private quickVoiceChatInProgressKey = CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); + private inlineVoiceChatInProgressKey = CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.bindTo(this.contextKeyService); + private voiceChatInViewInProgressKey = CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS.bindTo(this.contextKeyService); + private voiceChatInEditorInProgressKey = CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS.bindTo(this.contextKeyService); + private currentVoiceChatSession: ActiveVoiceChatSession | undefined = undefined; private voiceChatSessionIds = 0; @@ -199,8 +235,8 @@ class VoiceChatSessions { const cts = new CancellationTokenSource(); this.currentVoiceChatSession.disposables.add(toDisposable(() => cts.dispose(true))); - this.currentVoiceChatSession.disposables.add(controller.onDidAcceptInput(() => this.stop(voiceChatSessionId))); - this.currentVoiceChatSession.disposables.add(controller.onDidCancelInput(() => this.stop(voiceChatSessionId))); + this.currentVoiceChatSession.disposables.add(controller.onDidAcceptInput(() => this.stop(voiceChatSessionId, controller.context))); + this.currentVoiceChatSession.disposables.add(controller.onDidCancelInput(() => this.stop(voiceChatSessionId, controller.context))); controller.updateInput(''); controller.focusInput(); @@ -208,7 +244,7 @@ class VoiceChatSessions { this.voiceChatGettingReadyKey.set(true); const onDidTranscribe = await this.voiceRecognitionService.transcribe(cts.token, { - onDidCancel: () => this.stop(voiceChatSessionId) + onDidCancel: () => this.stop(voiceChatSessionId, controller.context) }); if (cts.token.isCancellationRequested) { @@ -218,6 +254,21 @@ class VoiceChatSessions { this.voiceChatGettingReadyKey.set(false); this.voiceChatInProgressKey.set(true); + switch (controller.context) { + case 'inline': + this.inlineVoiceChatInProgressKey.set(true); + break; + case 'quick': + this.quickVoiceChatInProgressKey.set(true); + break; + case 'view': + this.voiceChatInViewInProgressKey.set(true); + break; + case 'editor': + this.voiceChatInEditorInProgressKey.set(true); + break; + } + this.registerTranscriptionListener(this.currentVoiceChatSession, onDidTranscribe); } @@ -243,7 +294,6 @@ class VoiceChatSessions { } else { session.controller.updateInput(text); } - } })); } @@ -262,10 +312,11 @@ class VoiceChatSessions { ); } - stop(voiceChatSessionId = this.voiceChatSessionIds): void { + stop(voiceChatSessionId = this.voiceChatSessionIds, context?: VoiceChatSessionContext): void { if ( !this.currentVoiceChatSession || - this.voiceChatSessionIds !== voiceChatSessionId + this.voiceChatSessionIds !== voiceChatSessionId || + (context && this.currentVoiceChatSession.controller.context !== context) ) { return; } @@ -275,6 +326,11 @@ class VoiceChatSessions { this.voiceChatGettingReadyKey.set(false); this.voiceChatInProgressKey.set(false); + + this.quickVoiceChatInProgressKey.set(false); + this.inlineVoiceChatInProgressKey.set(false); + this.voiceChatInViewInProgressKey.set(false); + this.voiceChatInEditorInProgressKey.set(false); } accept(voiceChatSessionId = this.voiceChatSessionIds): void { @@ -381,16 +437,16 @@ class StartVoiceChatAction extends Action2 { value: localize('workbench.action.chat.startVoiceChat', "Start Voice Chat"), original: 'Start Voice Chat' }, - icon: Codicon.record, + icon: Codicon.mic, precondition: CONTEXT_VOICE_CHAT_GETTING_READY.negate(), menu: [{ id: MenuId.ChatExecute, - when: CONTEXT_VOICE_CHAT_IN_PROGRESS.negate(), + when: ContextKeyExpr.and(CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS.negate(), CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS.negate(), CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS.negate()), group: 'navigation', order: -1 }, { id: MENU_INLINE_CHAT_WIDGET, - when: CONTEXT_VOICE_CHAT_IN_PROGRESS.negate(), + when: CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS.negate(), group: 'main', order: -1 }] @@ -406,6 +462,9 @@ class StartVoiceChatAction extends Action2 { // from a toolbar within the chat widget, then make sure // to move focus into the input field so that the controller // is properly retrieved + // TODO@bpasero this will actually not work if the button + // is clicked from the inline editor while focus is in a + // chat input field in a view or picker context.widget.focusInput(); } @@ -437,16 +496,136 @@ class StopVoiceChatAction extends Action2 { when: CONTEXT_VOICE_CHAT_IN_PROGRESS, primary: KeyCode.Escape }, - precondition: CONTEXT_VOICE_CHAT_IN_PROGRESS, + precondition: CONTEXT_VOICE_CHAT_IN_PROGRESS + }); + } + + run(accessor: ServicesAccessor): void { + VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).stop(); + } +} + +class StopVoiceChatInChatViewAction extends Action2 { + + static readonly ID = 'workbench.action.chat.stopVoiceChatInChatView'; + + constructor() { + super({ + id: StopVoiceChatInChatViewAction.ID, + title: { + value: localize('workbench.action.chat.stopVoiceChatInChatView.label', "Stop Voice Chat (Chat View)"), + original: 'Stop Voice Chat (Chat View)' + }, + category: CHAT_CATEGORY, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib + 100, + when: CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS, + primary: KeyCode.Escape + }, + precondition: CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS, icon: spinningLoading, menu: [{ id: MenuId.ChatExecute, - when: CONTEXT_VOICE_CHAT_IN_PROGRESS, + when: CONTEXT_VOICE_CHAT_IN_VIEW_IN_PROGRESS, group: 'navigation', order: -1 - }, { + }] + }); + } + + run(accessor: ServicesAccessor): void { + VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).stop(undefined, 'view'); + } +} + +class StopVoiceChatInChatEditorAction extends Action2 { + + static readonly ID = 'workbench.action.chat.stopVoiceChatInChatEditor'; + + constructor() { + super({ + id: StopVoiceChatInChatEditorAction.ID, + title: { + value: localize('workbench.action.chat.stopVoiceChatInChatEditor.label', "Stop Voice Chat (Chat Editor)"), + original: 'Stop Voice Chat (Chat Editor)' + }, + category: CHAT_CATEGORY, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib + 100, + when: CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS, + primary: KeyCode.Escape + }, + precondition: CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS, + icon: spinningLoading, + menu: [{ + id: MenuId.ChatExecute, + when: CONTEXT_VOICE_CHAT_IN_EDITOR_IN_PROGRESS, + group: 'navigation', + order: -1 + }] + }); + } + + run(accessor: ServicesAccessor): void { + VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).stop(undefined, 'editor'); + } +} + +class StopQuickVoiceChatAction extends Action2 { + + static readonly ID = 'workbench.action.chat.stopQuickVoiceChat'; + + constructor() { + super({ + id: StopQuickVoiceChatAction.ID, + title: { + value: localize('workbench.action.chat.stopQuickVoiceChat.label', "Stop Voice Chat (Quick Chat)"), + original: 'Stop Voice Chat (Quick Chat)' + }, + category: CHAT_CATEGORY, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib + 100, + when: CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS, + primary: KeyCode.Escape + }, + precondition: CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS, + icon: spinningLoading, + menu: [{ + id: MenuId.ChatExecute, + when: CONTEXT_QUICK_VOICE_CHAT_IN_PROGRESS, + group: 'navigation', + order: -1 + }] + }); + } + + run(accessor: ServicesAccessor): void { + VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).stop(undefined, 'quick'); + } +} + +class StopInlineVoiceChatAction extends Action2 { + + static readonly ID = 'workbench.action.chat.stopInlineVoiceChat'; + + constructor() { + super({ + id: StopInlineVoiceChatAction.ID, + title: { + value: localize('workbench.action.chat.stopInlineVoiceChat.label', "Stop Voice Chat (Inline Editor)"), + original: 'Stop Voice Chat (Inline Editor)' + }, + category: CHAT_CATEGORY, + keybinding: { + weight: KeybindingWeight.WorkbenchContrib + 100, + when: CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS, + primary: KeyCode.Escape + }, + precondition: CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS, + icon: spinningLoading, + menu: [{ id: MENU_INLINE_CHAT_WIDGET, - when: CONTEXT_VOICE_CHAT_IN_PROGRESS, + when: CONTEXT_INLINE_VOICE_CHAT_IN_PROGRESS, group: 'main', order: -1 }] @@ -454,7 +633,7 @@ class StopVoiceChatAction extends Action2 { } run(accessor: ServicesAccessor): void { - VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).stop(); + VoiceChatSessions.getInstance(accessor.get(IInstantiationService)).stop(undefined, 'inline'); } } @@ -489,5 +668,10 @@ export function registerVoiceChatActions() { registerAction2(StartVoiceChatAction); registerAction2(StopVoiceChatAction); registerAction2(StopVoiceChatAndSubmitAction); + + registerAction2(StopVoiceChatInChatViewAction); + registerAction2(StopVoiceChatInChatEditorAction); + registerAction2(StopQuickVoiceChatAction); + registerAction2(StopInlineVoiceChatAction); } } From 204d0b30381bfaf3300061c42466bb5f3c0ed1a0 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 31 Aug 2023 10:05:57 +0200 Subject: [PATCH 395/607] window.title setting feedback (fix #191579) (#191885) --- 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 e41769f836d..a0aa8d51b7b 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -533,7 +533,7 @@ const registry = Registry.as(ConfigurationExtensions.Con // Window - let windowTitleDescription = localize('windowTitle', "Controls the window title based on the active editor. Variables are substituted based on the context:"); + let windowTitleDescription = localize('windowTitle', "Controls the window title based on the current context such as the opened workspace or active editor. Variables are substituted based on the context:"); windowTitleDescription += '\n- ' + [ localize('activeEditorShort', "`${activeEditorShort}`: the file name (e.g. myFile.txt)."), localize('activeEditorMedium', "`${activeEditorMedium}`: the path of the file relative to the workspace folder (e.g. myFolder/myFileFolder/myFile.txt)."), From 8800b431813386df04a1cae1b7e592e9462b17d7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 31 Aug 2023 10:41:37 +0200 Subject: [PATCH 396/607] some Some grid operations cause the `activeElement` to get lost (fix #189256) (#191886) --- src/vs/workbench/browser/parts/editor/editorActions.ts | 3 ++- src/vs/workbench/browser/parts/editor/editorPart.ts | 9 --------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index e3e5c11bfa6..149b76d8df9 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -2264,7 +2264,8 @@ abstract class AbstractCreateEditorGroupAction extends Action2 { override async run(accessor: ServicesAccessor): Promise { const editorGroupService = accessor.get(IEditorGroupsService); - editorGroupService.addGroup(editorGroupService.activeGroup, this.direction, { activate: true }); + const group = editorGroupService.addGroup(editorGroupService.activeGroup, this.direction, { activate: true }); + group.focus(); } } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 8b733caa867..c424f439a4f 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -515,21 +515,12 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroupView { const locationView = this.assertGroupView(location); - const restoreFocus = this.shouldRestoreFocus(locationView.element); - const group = this.doAddGroup(locationView, direction); if (options?.activate) { this.doSetGroupActive(group); } - // Restore focus if we had it previously after completing the grid - // operation. That operation might cause reparenting of grid views - // which moves focus to the element otherwise. - if (restoreFocus) { - locationView.focus(); - } - return group; } From 1ebb673280bc69f979e56f1c4be49e79bad73530 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 31 Aug 2023 11:02:57 +0200 Subject: [PATCH 397/607] Linux: Notifications Accessible View only opens when focusing with mouse (fix #191705) (#191888) --- .../parts/notifications/notificationsCommands.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 1f3ec683798..52910366b5f 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -68,9 +68,19 @@ export function getNotificationFromContext(listService: IListService, context?: const list = listService.lastFocusedList; if (list instanceof WorkbenchList) { - const focusedElement = list.getFocusedElements()[0]; - if (isNotificationViewItem(focusedElement)) { - return focusedElement; + let element = list.getFocusedElements()[0]; + if (!isNotificationViewItem(element)) { + if (list.isDOMFocused()) { + // the notification list might have received focus + // via keyboard and might not have a focussed element. + // in that case just return the first element + // https://github.com/microsoft/vscode/issues/191705 + element = list.element(0); + } + } + + if (isNotificationViewItem(element)) { + return element; } } From fffb813460a4c51dc04f1391600c9f2f5018d13d Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 31 Aug 2023 11:19:33 +0200 Subject: [PATCH 398/607] adding code --- .../client/src/jsonClient.ts | 24 +++++++++---------- .../server/src/jsonServer.ts | 21 +++++++++++++--- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index a801245a46d..ac918999e1a 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -8,12 +8,12 @@ export type JSONLanguageStatus = { schemas: string[] }; import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, ColorInformation, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, CodeActionKind, CodeAction + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, CodeActionContext, CodeAction, Command, } from 'vscode'; import { LanguageClientOptions, RequestType, NotificationType, FormattingOptions as LSPFormattingOptions, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, BaseLanguageClient, ProvideFoldingRangeSignature, ProvideDocumentSymbolsSignature, ProvideDocumentColorsSignature + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, BaseLanguageClient, ProvideFoldingRangeSignature, ProvideDocumentSymbolsSignature, ProvideDocumentColorsSignature, ProvideCodeActionsSignature } from 'vscode-languageclient'; @@ -172,7 +172,6 @@ export async function startClient(context: ExtensionContext, newLanguageClient: toDispose.push(commands.registerCommand('json.sort', async () => { - if (isClientReady) { const textEditor = window.activeTextEditor; if (textEditor) { @@ -303,16 +302,15 @@ export async function startClient(context: ExtensionContext, newLanguageClient: } return checkLimit(r); }, - provideCodeActions(doc) { - console.log('doc : ', doc); - console.log('inside of provideCodeActions'); - const codeActions: CodeAction[] = []; - const sortCodeAction = new CodeAction('Sort JSON', CodeActionKind.Source); - sortCodeAction.command = { - command: 'json.sort', - title: 'Sort JSON' - }; - return codeActions; + provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken, next: ProvideCodeActionsSignature) { + console.log('inside of provide code actions'); + console.log('next : ', next); + const r = next(document, range, context, token); + console.log('r : ', r); + if (isThenable<(Command | CodeAction)[] | null | undefined>(r)) { + return r; + } + return r; } } }; diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 0282e6fa939..ae14131082c 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -6,12 +6,12 @@ import { Connection, TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic, CodeActionKind } from 'vscode-languageserver'; import { runSafe, runSafeAsync } from './utils/runner'; import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; -import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position, SortOptions } from 'vscode-json-languageservice'; +import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position, SortOptions, CodeAction } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { Utils, URI } from 'vscode-uri'; @@ -188,7 +188,8 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) documentSelector: null, interFileDependencies: false, workspaceDiagnostics: false - } + }, + codeActionProvider: true }; return { capabilities }; @@ -411,6 +412,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) connection.onDocumentSymbol((documentSymbolParams, token) => { return runSafe(runtime, () => { + console.log('inside of on document symbol'); const document = documents.get(documentSymbolParams.textDocument.uri); if (document) { const jsonDocument = getJSONDocument(document); @@ -424,6 +426,19 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); }); + connection.onCodeAction((_codeActionParams, token) => { + return runSafe(runtime, () => { + console.log('Inside of on code action'); + const codeActions: CodeAction[] = []; + const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source); + sortCodeAction.command = { + command: 'json.sort', + title: 'Sort JSON' + }; + return codeActions; + }, [], `Error while retrieving code actions`, token); + }); + function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): TextEdit[] { options.keepLines = keepLinesEnabled; From 31b6e070b2e97a7518cda7af1011b71b6383029f Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 12:11:59 +0200 Subject: [PATCH 399/607] use CSS mask and icon-foreground color so that customized foreground colors also work for CSS --- .../browser/menuEntryActionViewItem.ts | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 356ff5e1013..5a053caa296 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, addDisposableListener, append, asCSSUrl, EventType, ModifierKeyEmitter, prepend } from 'vs/base/browser/dom'; +import { $, addDisposableListener, append, asCSSUrl, EventType, ModifierKeyEmitter, prepend, reset } from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionViewItem, BaseActionViewItem, SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { DropdownMenuActionViewItem, IDropdownMenuActionViewItemOptions } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; @@ -257,17 +257,26 @@ export class MenuEntryActionViewItem extends ActionViewItem { }); } else { - // icon path/url - label.style.backgroundImage = ( - isDark(this._themeService.getColorTheme().type) - ? asCSSUrl(icon.dark) - : asCSSUrl(icon.light) - ); + // icon path/url - add special element with SVG-mask and icon color background + const svgUrl = isDark(this._themeService.getColorTheme().type) + ? asCSSUrl(icon.dark) + : asCSSUrl(icon.light); + + const svgIcon = $('span'); + svgIcon.style.webkitMask = `${svgUrl} no-repeat 50% 50%`; + svgIcon.style.webkitMaskOrigin = 'padding'; + svgIcon.style.background = 'var(--vscode-icon-foreground)'; + svgIcon.style.display = 'inline-block'; + svgIcon.style.width = '100%'; + svgIcon.style.height = '100%'; + + label.appendChild(svgIcon); label.classList.add('icon'); + this._itemClassDispose.value = combinedDisposable( toDisposable(() => { - label.style.backgroundImage = ''; label.classList.remove('icon'); + reset(label); }), this._themeService.onDidColorThemeChange(() => { // refresh when the theme changes in case we go between dark <-> light From 5306d2b89814ca1e52d4d9e7929dbc8c8cb18157 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 12:21:38 +0200 Subject: [PATCH 400/607] don't inherit color for codicons from parent but use theme defined color --- src/vs/workbench/browser/media/part.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css index bec9b2a6d75..a0628d68945 100644 --- a/src/vs/workbench/browser/media/part.css +++ b/src/vs/workbench/browser/media/part.css @@ -82,10 +82,6 @@ display: none; } -.monaco-workbench .part > .title > .title-actions .action-label.codicon { - color: inherit; -} - .monaco-workbench .part > .content { font-size: 13px; } From 07aacd25bbd0463b29e7274b46d2f1510d3ad735 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Thu, 31 Aug 2023 12:22:11 +0200 Subject: [PATCH 401/607] Initialize all services as soon as the first service is needed (#191890) Fixes microsoft/monaco-editor#4120: initialize all services as soon as the first service is needed --- src/vs/editor/standalone/browser/standaloneServices.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index e7297361013..01f2c6987ac 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -1114,6 +1114,9 @@ export module StandaloneServices { serviceCollection.set(IInstantiationService, instantiationService); export function get(serviceId: ServiceIdentifier): T { + if (!initialized) { + initialize({}); + } const r = serviceCollection.get(serviceId); if (!r) { throw new Error('Missing service ' + serviceId); From 596a9a8926eafd92b4b5a127cb6740da710f4c79 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 12:24:29 +0200 Subject: [PATCH 402/607] removed unneccessary mask property, make FF happy --- src/vs/platform/actions/browser/menuEntryActionViewItem.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 5a053caa296..c6e7a926213 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -263,8 +263,7 @@ export class MenuEntryActionViewItem extends ActionViewItem { : asCSSUrl(icon.light); const svgIcon = $('span'); - svgIcon.style.webkitMask = `${svgUrl} no-repeat 50% 50%`; - svgIcon.style.webkitMaskOrigin = 'padding'; + svgIcon.style.webkitMask = svgIcon.style.mask = `${svgUrl} no-repeat 50% 50%`; svgIcon.style.background = 'var(--vscode-icon-foreground)'; svgIcon.style.display = 'inline-block'; svgIcon.style.width = '100%'; From 6b49d155ca9f2547e9e8ddfdb324db47da97411b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 31 Aug 2023 12:21:07 +0200 Subject: [PATCH 403/607] Fixes #191892 --- src/vs/editor/common/config/diffEditor.ts | 4 ++-- .../config/editorConfigurationSchema.ts | 24 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/common/config/diffEditor.ts b/src/vs/editor/common/config/diffEditor.ts index 2f2bc06f2d2..2a62c479848 100644 --- a/src/vs/editor/common/config/diffEditor.ts +++ b/src/vs/editor/common/config/diffEditor.ts @@ -5,7 +5,7 @@ import { ValidDiffEditorBaseOptions } from 'vs/editor/common/config/editorOptions'; -export const diffEditorDefaultOptions: ValidDiffEditorBaseOptions = { +export const diffEditorDefaultOptions = { enableSplitViewResizing: true, splitViewDefaultRatio: 0.5, renderSideBySide: true, @@ -34,4 +34,4 @@ export const diffEditorDefaultOptions: ValidDiffEditorBaseOptions = { onlyShowAccessibleDiffViewer: false, renderSideBySideInlineBreakpoint: 900, useInlineViewWhenSpaceIsLimited: true, -}; +} satisfies ValidDiffEditorBaseOptions; diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 845adcd40ac..684ec85c33b 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -151,53 +151,53 @@ const editorConfiguration: IConfigurationNode = { }, 'diffEditor.maxComputationTime': { type: 'number', - default: 5000, + default: diffEditorDefaultOptions.maxComputationTime, description: nls.localize('maxComputationTime', "Timeout in milliseconds after which diff computation is cancelled. Use 0 for no timeout.") }, 'diffEditor.maxFileSize': { type: 'number', - default: 50, + default: diffEditorDefaultOptions.maxFileSize, description: nls.localize('maxFileSize', "Maximum file size in MB for which to compute diffs. Use 0 for no limit.") }, 'diffEditor.renderSideBySide': { type: 'boolean', - default: true, + default: diffEditorDefaultOptions.renderSideBySide, description: nls.localize('sideBySide', "Controls whether the diff editor shows the diff side by side or inline.") }, 'diffEditor.renderSideBySideInlineBreakpoint': { type: 'number', - default: true, + default: diffEditorDefaultOptions.renderSideBySideInlineBreakpoint, description: nls.localize('renderSideBySideInlineBreakpoint', "If the diff editor width is smaller than this value, the inline view is used.") }, 'diffEditor.useInlineViewWhenSpaceIsLimited': { type: 'boolean', - default: true, + default: diffEditorDefaultOptions.useInlineViewWhenSpaceIsLimited, description: nls.localize('useInlineViewWhenSpaceIsLimited', "If enabled and the editor width is too small, the inline view is used.") }, 'diffEditor.renderMarginRevertIcon': { type: 'boolean', - default: true, + default: diffEditorDefaultOptions.renderMarginRevertIcon, description: nls.localize('renderMarginRevertIcon', "When enabled, the diff editor shows arrows in its glyph margin to revert changes.") }, 'diffEditor.ignoreTrimWhitespace': { type: 'boolean', - default: true, + default: diffEditorDefaultOptions.ignoreTrimWhitespace, description: nls.localize('ignoreTrimWhitespace', "When enabled, the diff editor ignores changes in leading or trailing whitespace.") }, 'diffEditor.renderIndicators': { type: 'boolean', - default: true, + default: diffEditorDefaultOptions.renderIndicators, description: nls.localize('renderIndicators', "Controls whether the diff editor shows +/- indicators for added/removed changes.") }, 'diffEditor.codeLens': { type: 'boolean', - default: false, + default: diffEditorDefaultOptions.diffCodeLens, description: nls.localize('codeLens', "Controls whether the editor shows CodeLens.") }, 'diffEditor.wordWrap': { type: 'string', enum: ['off', 'on', 'inherit'], - default: 'inherit', + default: diffEditorDefaultOptions.diffWordWrap, markdownEnumDescriptions: [ nls.localize('wordWrap.off', "Lines will never wrap."), nls.localize('wordWrap.on', "Lines will wrap at the viewport width."), @@ -239,7 +239,7 @@ const editorConfiguration: IConfigurationNode = { }, 'diffEditor.experimental.showMoves': { type: 'boolean', - default: false, + default: diffEditorDefaultOptions.experimental.showMoves, markdownDescription: nls.localize('showMoves', "Controls whether the diff editor should show detected code moves. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`') }, 'diffEditor.experimental.useVersion2': { @@ -250,7 +250,7 @@ const editorConfiguration: IConfigurationNode = { }, 'diffEditor.experimental.showEmptyDecorations': { type: 'boolean', - default: true, + default: diffEditorDefaultOptions.experimental.showEmptyDecorations, description: nls.localize('showEmptyDecorations', "Controls whether the diff editor shows empty decorations to see where characters got inserted or deleted."), } } From 06fdc0a6339c1b10c4b0d9739191d7606d43e9cb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 31 Aug 2023 15:10:14 +0200 Subject: [PATCH 404/607] unsure how to register the provider --- .../client/src/jsonClient.ts | 52 +++++++++++++++---- .../server/src/jsonServer.ts | 11 ++-- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index ac918999e1a..171d1c33d44 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -8,7 +8,7 @@ export type JSONLanguageStatus = { schemas: string[] }; import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, ColorInformation, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, CodeActionContext, CodeAction, Command, + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, CodeActionContext, CodeAction, Command, CodeActionProvider, Selection, CodeActionKind, } from 'vscode'; import { LanguageClientOptions, RequestType, NotificationType, FormattingOptions as LSPFormattingOptions, @@ -172,6 +172,7 @@ export async function startClient(context: ExtensionContext, newLanguageClient: toDispose.push(commands.registerCommand('json.sort', async () => { + if (isClientReady) { const textEditor = window.activeTextEditor; if (textEditor) { @@ -189,6 +190,35 @@ export async function startClient(context: ExtensionContext, newLanguageClient: } })); + class JSONCodeActionProvider implements CodeActionProvider { + + provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(CodeAction | Command)[]> { + console.log('inside of provide code actions'); + const codeActions: CodeAction[] = []; + const sortCodeAction = new CodeAction('Sort JSON', CodeActionKind.Source); + sortCodeAction.command = { + command: 'json.sort', + title: 'Sort JSON' + }; + return codeActions; + } + } + + languages.registerCodeActionsProvider('*', new JSONCodeActionProvider()); + + // connection.onCodeAction((_codeActionParams, token) => { + // return runSafe(runtime, () => { + // console.log('Inside of on code action'); + // const codeActions: CodeAction[] = []; + // const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source); + // sortCodeAction.command = { + // command: 'json.sort', + // title: 'Sort JSON' + // }; + // return codeActions; + // }, [], `Error while retrieving code actions`, token); + // }); + // Options to control the language client const clientOptions: LanguageClientOptions = { // Register the server for json documents @@ -302,16 +332,16 @@ export async function startClient(context: ExtensionContext, newLanguageClient: } return checkLimit(r); }, - provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken, next: ProvideCodeActionsSignature) { - console.log('inside of provide code actions'); - console.log('next : ', next); - const r = next(document, range, context, token); - console.log('r : ', r); - if (isThenable<(Command | CodeAction)[] | null | undefined>(r)) { - return r; - } - return r; - } + // provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken, next: ProvideCodeActionsSignature) { + // console.log('inside of provide code actions'); + // console.log('next : ', next); + // const r = next(document, range, context, token); + // console.log('r : ', r); + // if (isThenable<(Command | CodeAction)[] | null | undefined>(r)) { + // return r; + // } + // return r; + // } } }; diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index ae14131082c..82c6a3cc317 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -6,12 +6,12 @@ import { Connection, TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic, CodeActionKind + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit, DocumentFormattingRequest, TextDocumentIdentifier, FormattingOptions, Diagnostic, CodeAction, CodeActionKind } from 'vscode-languageserver'; import { runSafe, runSafeAsync } from './utils/runner'; import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnosticsPushSupport } from './utils/validation'; -import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position, SortOptions, CodeAction } from 'vscode-json-languageservice'; +import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position, SortOptions } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { Utils, URI } from 'vscode-uri'; @@ -412,7 +412,6 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) connection.onDocumentSymbol((documentSymbolParams, token) => { return runSafe(runtime, () => { - console.log('inside of on document symbol'); const document = documents.get(documentSymbolParams.textDocument.uri); if (document) { const jsonDocument = getJSONDocument(document); @@ -426,6 +425,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); }); + // connection.onCodeAction((_codeActionParams, token) => { return runSafe(runtime, () => { console.log('Inside of on code action'); @@ -439,6 +439,11 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, [], `Error while retrieving code actions`, token); }); + connection.onCodeActionResolve(async (codeAction, token) => { + return codeAction; + }); + // + function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): TextEdit[] { options.keepLines = keepLinesEnabled; From 70694338048e575132149ac39b91102cb552d6f9 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 31 Aug 2023 15:34:10 +0200 Subject: [PATCH 405/607] cleaning the code --- .../client/src/jsonClient.ts | 45 ++----------------- .../server/src/jsonServer.ts | 30 ++++++------- 2 files changed, 16 insertions(+), 59 deletions(-) diff --git a/extensions/json-language-features/client/src/jsonClient.ts b/extensions/json-language-features/client/src/jsonClient.ts index 171d1c33d44..3f191f165cf 100644 --- a/extensions/json-language-features/client/src/jsonClient.ts +++ b/extensions/json-language-features/client/src/jsonClient.ts @@ -8,12 +8,12 @@ export type JSONLanguageStatus = { schemas: string[] }; import { workspace, window, languages, commands, ExtensionContext, extensions, Uri, ColorInformation, Diagnostic, StatusBarAlignment, TextEditor, TextDocument, FormattingOptions, CancellationToken, FoldingRange, - ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n, CodeActionContext, CodeAction, Command, CodeActionProvider, Selection, CodeActionKind, + ProviderResult, TextEdit, Range, Position, Disposable, CompletionItem, CompletionList, CompletionContext, Hover, MarkdownString, FoldingContext, DocumentSymbol, SymbolInformation, l10n } from 'vscode'; import { LanguageClientOptions, RequestType, NotificationType, FormattingOptions as LSPFormattingOptions, DidChangeConfigurationNotification, HandleDiagnosticsSignature, ResponseError, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, BaseLanguageClient, ProvideFoldingRangeSignature, ProvideDocumentSymbolsSignature, ProvideDocumentColorsSignature, ProvideCodeActionsSignature + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, ProvideHoverSignature, BaseLanguageClient, ProvideFoldingRangeSignature, ProvideDocumentSymbolsSignature, ProvideDocumentColorsSignature } from 'vscode-languageclient'; @@ -190,35 +190,6 @@ export async function startClient(context: ExtensionContext, newLanguageClient: } })); - class JSONCodeActionProvider implements CodeActionProvider { - - provideCodeActions(document: TextDocument, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<(CodeAction | Command)[]> { - console.log('inside of provide code actions'); - const codeActions: CodeAction[] = []; - const sortCodeAction = new CodeAction('Sort JSON', CodeActionKind.Source); - sortCodeAction.command = { - command: 'json.sort', - title: 'Sort JSON' - }; - return codeActions; - } - } - - languages.registerCodeActionsProvider('*', new JSONCodeActionProvider()); - - // connection.onCodeAction((_codeActionParams, token) => { - // return runSafe(runtime, () => { - // console.log('Inside of on code action'); - // const codeActions: CodeAction[] = []; - // const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source); - // sortCodeAction.command = { - // command: 'json.sort', - // title: 'Sort JSON' - // }; - // return codeActions; - // }, [], `Error while retrieving code actions`, token); - // }); - // Options to control the language client const clientOptions: LanguageClientOptions = { // Register the server for json documents @@ -331,17 +302,7 @@ export async function startClient(context: ExtensionContext, newLanguageClient: return r.then(checkLimit); } return checkLimit(r); - }, - // provideCodeActions(document: TextDocument, range: Range, context: CodeActionContext, token: CancellationToken, next: ProvideCodeActionsSignature) { - // console.log('inside of provide code actions'); - // console.log('next : ', next); - // const r = next(document, range, context, token); - // console.log('r : ', r); - // if (isThenable<(Command | CodeAction)[] | null | undefined>(r)) { - // return r; - // } - // return r; - // } + } } }; diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 82c6a3cc317..d88b80587dc 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -425,25 +425,21 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, [], `Error while computing document symbols for ${documentSymbolParams.textDocument.uri}`, token); }); - // - connection.onCodeAction((_codeActionParams, token) => { - return runSafe(runtime, () => { - console.log('Inside of on code action'); - const codeActions: CodeAction[] = []; - const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source); - sortCodeAction.command = { - command: 'json.sort', - title: 'Sort JSON' - }; - return codeActions; - }, [], `Error while retrieving code actions`, token); + connection.onCodeAction((codeActionParams, token) => { + return runSafeAsync(runtime, async () => { + const document = documents.get(codeActionParams.textDocument.uri); + if (document) { + const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source); + sortCodeAction.command = { + command: 'json.sort', + title: 'Sort JSON' + }; + return [sortCodeAction]; + } + return []; + }, [], `Error while computing code actions for ${codeActionParams.textDocument.uri}`, token); }); - connection.onCodeActionResolve(async (codeAction, token) => { - return codeAction; - }); - // - function onFormat(textDocument: TextDocumentIdentifier, range: Range | undefined, options: FormattingOptions): TextEdit[] { options.keepLines = keepLinesEnabled; From ab975ebe2868796ef2847b5fe4389a078f0c1cd5 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Thu, 31 Aug 2023 16:02:06 +0200 Subject: [PATCH 406/607] adding also sort and json into the name --- extensions/json-language-features/server/src/jsonServer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index d88b80587dc..9b353d913ed 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -429,7 +429,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) return runSafeAsync(runtime, async () => { const document = documents.get(codeActionParams.textDocument.uri); if (document) { - const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source); + const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source.concat('.sort', '.json')); sortCodeAction.command = { command: 'json.sort', title: 'Sort JSON' From 170f7cd76e2d92bf87bd6c7efce4c1d9c2cb850d Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 16:46:25 +0200 Subject: [PATCH 407/607] fix some, but not all leaks wrt DebugSession --- .../workbench/api/browser/mainThreadDebugService.ts | 5 +++++ .../workbench/contrib/debug/browser/callStackView.ts | 4 ++-- .../workbench/contrib/debug/browser/debugService.ts | 11 ++++++++--- src/vs/workbench/contrib/debug/common/debugModel.ts | 7 +++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 6b833ab272e..5ac499698fc 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -129,6 +129,11 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb const handle = this._debugAdaptersHandleCounter++; const da = new ExtensionHostDebugAdapter(this, handle, this._proxy, session); this._debugAdapters.set(handle, da); + + const d = session.onDidEndAdapter(e => { + this._debugAdapters.delete(handle); + d.dispose(); + }); return da; } diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index b3e3dc26f1f..08b531db334 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -280,7 +280,7 @@ export class CallStackView extends ViewPane { }); this.tree.setInput(this.debugService.getModel()); - + this._register(this.tree); this._register(this.tree.onDidOpen(async e => { if (this.ignoreSelectionChangedEvent) { return; @@ -461,7 +461,7 @@ export class CallStackView extends ViewPane { const contextKeyService = this.contextKeyService.createOverlay(overlay); const menu = this.menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService); createAndFillInContextMenuActions(menu, { arg: getContextForContributedActions(element), shouldForwardArgs: true }, result, 'inline'); - + menu.dispose(); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => result.secondary, diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 0fad52f9739..0851c2b08a0 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -655,14 +655,16 @@ export class DebugService implements IDebugService { } } - private registerSessionListeners(session: IDebugSession): void { + private registerSessionListeners(session: DebugSession): void { const sessionRunningScheduler = new RunOnceScheduler(() => { // Do not immediatly defocus the stack frame if the session is running if (session.state === State.Running && this.viewModel.focusedSession === session) { this.viewModel.setFocus(undefined, this.viewModel.focusedThread, session, false); } }, 200); - this.disposables.add(session.onDidChangeState(() => { + const sessionStore = new DisposableStore(); + + sessionStore.add(session.onDidChangeState(() => { if (session.state === State.Running && this.viewModel.focusedSession === session) { sessionRunningScheduler.schedule(); } @@ -671,7 +673,7 @@ export class DebugService implements IDebugService { } })); - this.disposables.add(session.onDidEndAdapter(async adapterExitEvent => { + sessionStore.add(session.onDidEndAdapter(async adapterExitEvent => { if (adapterExitEvent) { if (adapterExitEvent.error) { @@ -696,6 +698,7 @@ export class DebugService implements IDebugService { } this.endInitializingState(); this.cancelTokens(session.getId()); + this.model.removeSession(session); this._onDidEndSession.fire(session); const focusedSession = this.viewModel.focusedSession; @@ -724,6 +727,8 @@ export class DebugService implements IDebugService { } this.model.removeExceptionBreakpointsForSession(session.getId()); + sessionStore.dispose(); + session.dispose(); })); } diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 50640fcf7e5..fbb4b9abd47 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -1263,6 +1263,13 @@ export class DebugModel extends Disposable implements IDebugModel { this._onDidChangeCallStack.fire(undefined); } + removeSession(session: IDebugSession): void { + const idx = this.sessions.indexOf(session); + if (idx >= 0) { + this.sessions.splice(idx, 1); + } + } + get onDidChangeBreakpoints(): Event { return this._onDidChangeBreakpoints.event; } From a5fabc665be6c92ab5e4537c91347a661f8a4918 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 31 Aug 2023 07:47:49 -0700 Subject: [PATCH 408/607] fix #186904 --- .../accessibility/browser/textAreaSyncAddon.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 9916a6f60d0..c779eef46dc 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -100,9 +100,9 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); return; } - if (!!currentCommand.commandStartX) { - this._currentCommand = commandLine.substring(currentCommand.commandStartX); - this._cursorX = buffer.cursorX - currentCommand.commandStartX; + if (!!currentCommand) { + this._currentCommand = commandLine.substring(currentCommand.commandStartX ?? 0); + this._cursorX = buffer.cursorX - (currentCommand.commandStartX ?? 0); } else { this._currentCommand = undefined; this._cursorX = undefined; From 9637c47fdc5498a5206470ea36e28977bf271f56 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Thu, 31 Aug 2023 08:03:54 -0700 Subject: [PATCH 409/607] check if mac --- .../accessibility/browser/textAreaSyncAddon.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index c779eef46dc..5c6d8579ac3 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -10,6 +10,7 @@ import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import type { Terminal, ITerminalAddon } from 'xterm'; import { debounce } from 'vs/base/common/decorators'; import { addDisposableListener } from 'vs/base/browser/dom'; +import { isMacintosh } from 'vs/base/common/platform'; export interface ITextAreaData { content: string; @@ -100,9 +101,10 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); return; } - if (!!currentCommand) { - this._currentCommand = commandLine.substring(currentCommand.commandStartX ?? 0); - this._cursorX = buffer.cursorX - (currentCommand.commandStartX ?? 0); + const startX = isMacintosh ? currentCommand.commandStartX : 0; + if (!!currentCommand && !!startX) { + this._currentCommand = commandLine.substring(startX); + this._cursorX = buffer.cursorX - startX; } else { this._currentCommand = undefined; this._cursorX = undefined; From 9ae54e1b973e511255d9ee2a7a4a22be71699dba Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 17:12:53 +0200 Subject: [PATCH 410/607] AsyncDataTree doesn't dispose its tree --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index 3b4d493144b..ad460ab3d1d 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -1061,6 +1061,7 @@ export class AsyncDataTree implements IDisposable dispose(): void { this.disposables.dispose(); + this.tree.dispose(); } } From 4862c21259815d4ab4b540fca331986acbb71d7b Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 17:13:22 +0200 Subject: [PATCH 411/607] avoid the global listener trap --- .../api/browser/mainThreadDebugService.ts | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 5ac499698fc..bb8ceab13b2 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableMap, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { URI as uri, UriComponents } from 'vs/base/common/uri'; import { IDebugService, IConfig, IDebugConfigurationProvider, IBreakpoint, IFunctionBreakpoint, IBreakpointData, IDebugAdapter, IDebugAdapterDescriptorFactory, IDebugSession, IDebugAdapterFactory, IDataBreakpoint, IDebugSessionOptions, IInstructionBreakpoint, DebugConfigurationProviderTriggerKind } from 'vs/workbench/contrib/debug/common/debug'; import { @@ -33,19 +33,35 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb @IDebugService private readonly debugService: IDebugService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDebugService); + + const sessionListeners = new DisposableMap(); + this._toDispose.add(sessionListeners); this._toDispose.add(debugService.onDidNewSession(session => { this._proxy.$acceptDebugSessionStarted(this.getSessionDto(session)); - this._toDispose.add(session.onDidChangeName(name => { + const store = sessionListeners.get(session); + store!.add(session.onDidChangeName(name => { this._proxy.$acceptDebugSessionNameChanged(this.getSessionDto(session), name); })); })); // Need to start listening early to new session events because a custom event can come while a session is initialising this._toDispose.add(debugService.onWillNewSession(session => { - this._toDispose.add(session.onDidCustomEvent(event => this._proxy.$acceptDebugSessionCustomEvent(this.getSessionDto(session), event))); + let store = sessionListeners.get(session); + if (!store) { + store = new DisposableStore(); + sessionListeners.set(session, store); + } + store.add(session.onDidCustomEvent(event => this._proxy.$acceptDebugSessionCustomEvent(this.getSessionDto(session), event))); })); this._toDispose.add(debugService.onDidEndSession(session => { this._proxy.$acceptDebugSessionTerminated(this.getSessionDto(session)); this._sessions.delete(session.getId()); + for (const [handle, value] of this._debugAdapters) { + if (value._session === session) { + this._debugAdapters.delete(handle); + // break; + } + } + sessionListeners.deleteAndDispose(session); })); this._toDispose.add(debugService.getViewModel().onDidFocusSession(session => { this._proxy.$acceptDebugSessionActiveChanged(this.getSessionDto(session)); @@ -129,11 +145,6 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb const handle = this._debugAdaptersHandleCounter++; const da = new ExtensionHostDebugAdapter(this, handle, this._proxy, session); this._debugAdapters.set(handle, da); - - const d = session.onDidEndAdapter(e => { - this._debugAdapters.delete(handle); - d.dispose(); - }); return da; } @@ -417,7 +428,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb */ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { - constructor(private readonly _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, private _session: IDebugSession) { + constructor(private readonly _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, readonly _session: IDebugSession) { super(); } From 414606b665b564d5ea4cce008531c7b23555bdd0 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 31 Aug 2023 17:30:16 +0200 Subject: [PATCH 412/607] Don't throw if view doesn't exist when visibility false (#191781) --- src/vs/workbench/api/common/extHostTreeViews.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 3f33b7d7b62..844b0b63eae 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -246,6 +246,9 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { $setVisible(treeViewId: string, isVisible: boolean): void { const treeView = this.treeViews.get(treeViewId); if (!treeView) { + if (!isVisible) { + return; + } throw new NoTreeViewError(treeViewId); } treeView.setVisible(isVisible); From 7ce48dafb0ee42643e76b4d7504f84539ec097fd Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 17:35:02 +0200 Subject: [PATCH 413/607] don't leak view pane via DND listener https://github.com/microsoft/vscode/issues/191899 --- .../browser/parts/views/viewPaneContainer.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 1f593cd65c3..f23c48b7db1 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -13,7 +13,7 @@ import { IAction } from 'vs/base/common/actions'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { combinedDisposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { combinedDisposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { assertIsDefined } from 'vs/base/common/types'; import 'vs/css!./media/paneviewlet'; import * as nls from 'vs/nls'; @@ -852,17 +852,18 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { leftBorder: isPanel ? asCssVariable(PANEL_SECTION_BORDER) : undefined }); - const disposable = combinedDisposable(pane, onDidFocus, onDidBlur, onDidChangeTitleArea, onDidChange, onDidChangeVisibility); - const paneItem: IViewPaneItem = { pane, disposable }; + const store = new DisposableStore(); + store.add(combinedDisposable(pane, onDidFocus, onDidBlur, onDidChangeTitleArea, onDidChange, onDidChangeVisibility)); + const paneItem: IViewPaneItem = { pane, disposable: store }; this.paneItems.splice(index, 0, paneItem); assertIsDefined(this.paneview).addPane(pane, size, index); let overlay: ViewPaneDropOverlay | undefined; - this._register(CompositeDragAndDropObserver.INSTANCE.registerDraggable(pane.draggableElement, () => { return { type: 'view', id: pane.id }; }, {})); + store.add(CompositeDragAndDropObserver.INSTANCE.registerDraggable(pane.draggableElement, () => { return { type: 'view', id: pane.id }; }, {})); - this._register(CompositeDragAndDropObserver.INSTANCE.registerTarget(pane.dropTargetElement, { + store.add(CompositeDragAndDropObserver.INSTANCE.registerTarget(pane.dropTargetElement, { onDragEnter: (e) => { if (!overlay) { const dropData = e.dragAndDropData.getData(); From 542264f0ef02e63949e20671c36e716b6e8c9ad7 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 31 Aug 2023 17:42:58 +0200 Subject: [PATCH 414/607] fix variables view never disposing its tree --- src/vs/workbench/contrib/debug/browser/variablesView.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index b7d7e3da4ff..81bb8f38c38 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -149,6 +149,7 @@ export class VariablesView extends ViewPane { forgetScopes = true; this.tree.updateChildren(); })); + this._register(this.tree); this._register(this.tree.onMouseDblClick(e => this.onMouseDblClick(e))); this._register(this.tree.onContextMenu(async e => await this.onContextMenu(e))); From 68a8e14d305982c6468019082454681a1bd76c76 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 31 Aug 2023 08:43:49 -0700 Subject: [PATCH 415/607] fix issue --- .../accessibility/browser/textAreaSyncAddon.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 5c6d8579ac3..8e6e18b28e5 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -101,8 +101,8 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); return; } - const startX = isMacintosh ? currentCommand.commandStartX : 0; - if (!!currentCommand && !!startX) { + const startX = isMacintosh || currentCommand.commandStartX !== undefined ? currentCommand.commandStartX : 0; + if (!!currentCommand && startX !== undefined) { this._currentCommand = commandLine.substring(startX); this._cursorX = buffer.cursorX - startX; } else { From 6ca26d992b6de880cae1e5eb975ce88582260caa Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 31 Aug 2023 09:08:40 -0700 Subject: [PATCH 416/607] different udf check --- .../accessibility/browser/textAreaSyncAddon.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index 8e6e18b28e5..b5e800a788d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -10,7 +10,6 @@ import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import type { Terminal, ITerminalAddon } from 'xterm'; import { debounce } from 'vs/base/common/decorators'; import { addDisposableListener } from 'vs/base/browser/dom'; -import { isMacintosh } from 'vs/base/common/platform'; export interface ITextAreaData { content: string; @@ -101,10 +100,9 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); return; } - const startX = isMacintosh || currentCommand.commandStartX !== undefined ? currentCommand.commandStartX : 0; - if (!!currentCommand && startX !== undefined) { - this._currentCommand = commandLine.substring(startX); - this._cursorX = buffer.cursorX - startX; + if (currentCommand?.commandStartX !== undefined) { + this._currentCommand = commandLine.substring(currentCommand.commandStartX); + this._cursorX = buffer.cursorX - currentCommand.commandStartX; } else { this._currentCommand = undefined; this._cursorX = undefined; From a3d842ed4d190fbcf30d2c76460379cd8bab8b50 Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Thu, 31 Aug 2023 09:29:53 -0700 Subject: [PATCH 417/607] Update src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts Co-authored-by: Daniel Imms <2193314+Tyriar@users.noreply.github.com> --- .../terminalContrib/accessibility/browser/textAreaSyncAddon.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts index b5e800a788d..33d4b52e956 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/textAreaSyncAddon.ts @@ -100,7 +100,7 @@ export class TextAreaSyncAddon extends Disposable implements ITerminalAddon { this._logService.debug(`TextAreaSyncAddon#updateCommandAndCursor: no line`); return; } - if (currentCommand?.commandStartX !== undefined) { + if (currentCommand.commandStartX !== undefined) { this._currentCommand = commandLine.substring(currentCommand.commandStartX); this._cursorX = buffer.cursorX - currentCommand.commandStartX; } else { From eec2fc723c952f18c7ca0005c2bcf8840c819d38 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 31 Aug 2023 13:10:26 -0700 Subject: [PATCH 418/607] Disable Local Server flow for REH (#191930) Because spinning up ports on the remote won't always work. Instead, we have the trusty device code flow. Fixes https://github.com/microsoft/vscode/issues/191866 Fixes https://github.com/microsoft/vscode/issues/191867 --- extensions/github-authentication/src/flows.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/github-authentication/src/flows.ts b/extensions/github-authentication/src/flows.ts index 5bc9d095385..1e988d92d30 100644 --- a/extensions/github-authentication/src/flows.ts +++ b/extensions/github-authentication/src/flows.ts @@ -200,7 +200,9 @@ const allFlows: IFlow[] = [ // other flows that work well. supportsGitHubEnterpriseServer: false, supportsHostedGitHubEnterprise: true, - supportsRemoteExtensionHost: true, + // Opening a port on the remote side can't be open in the browser on + // the client side so this flow won't work in remote extension hosts + supportsRemoteExtensionHost: false, // Web worker can't open a port to listen for the redirect supportsWebWorkerExtensionHost: false, // exchanging a code for a token requires a client secret From 065d4c1e23b278e2a277e80b23c07627b0efa4eb Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Thu, 31 Aug 2023 14:28:15 -0700 Subject: [PATCH 419/607] Do not show parent checkbox contents for ai generated workspaces (#191933) --- .../contrib/workspace/browser/workspace.contribution.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 077b248bc0f..864a19e1ce4 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -311,13 +311,12 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon this._register(this.workspaceTrustRequestService.onDidInitiateWorkspaceTrustRequestOnStartup(async () => { let titleString: string | undefined; - let checkboxString: string | undefined; let learnMoreString: string | undefined; let trustOption: string | undefined; let dontTrustOption: string | undefined; - if (await this.isAiGeneratedWorkspace() && this.productService.aiGeneratedWorkspaceTrust) { + const isAiGeneratedWorkspace = await this.isAiGeneratedWorkspace(); + if (isAiGeneratedWorkspace && this.productService.aiGeneratedWorkspaceTrust) { titleString = this.productService.aiGeneratedWorkspaceTrust.title; - checkboxString = this.productService.aiGeneratedWorkspaceTrust.checkboxText; learnMoreString = this.productService.aiGeneratedWorkspaceTrust.startupTrustRequestLearnMore; trustOption = this.productService.aiGeneratedWorkspaceTrust.trustOption; dontTrustOption = this.productService.aiGeneratedWorkspaceTrust.dontTrustOption; @@ -333,9 +332,9 @@ export class WorkspaceTrustUXHandler extends Disposable implements IWorkbenchCon const workspaceIdentifier = toWorkspaceIdentifier(this.workspaceContextService.getWorkspace()); const isSingleFolderWorkspace = isSingleFolderWorkspaceIdentifier(workspaceIdentifier); const isEmptyWindow = isEmptyWorkspaceIdentifier(workspaceIdentifier); - if (this.workspaceTrustManagementService.canSetParentFolderTrust()) { + if (!isAiGeneratedWorkspace && this.workspaceTrustManagementService.canSetParentFolderTrust()) { const name = basename(uriDirname((workspaceIdentifier as ISingleFolderWorkspaceIdentifier).uri)); - checkboxText = checkboxString ?? localize('checkboxString', "Trust the authors of all files in the parent folder '{0}'", name); + checkboxText = localize('checkboxString', "Trust the authors of all files in the parent folder '{0}'", name); } // Show Workspace Trust Start Dialog From 79277e0b8f045483a49f8a6834d476410c0d3cba Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 31 Aug 2023 14:34:22 -0700 Subject: [PATCH 420/607] Skip flakey smoke test (#191936) * Skip flakey smoke test ref https://github.com/microsoft/vscode/issues/191860 * skip at describe since there's only 1 test --- test/smoke/src/areas/extensions/extensions.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index 7a4875bcd7f..a8120cb12bf 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -7,7 +7,7 @@ import { Application, Logger } from '../../../../automation'; import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { - describe('Extensions', () => { + describe.skip('Extensions', () => { // Shared before/after handling installAllHandlers(logger); From 4c6dbcf90f338caae79babdf8d48e6524a86fbe7 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Thu, 31 Aug 2023 15:28:39 -0700 Subject: [PATCH 421/607] Add check to see if resource has file extension before setting FILE | FOLDER (#191923) * Set resource as FILE only if children are undefined * Add check to see if resource has extension before setting FileKind --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 8ba6d711809..4bfefbd3af3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -1200,7 +1200,8 @@ class ChatListTreeRenderer implements ICompressibleTreeRenderer, index: number, templateData: IChatListTreeRendererTemplate, height: number | undefined): void { templateData.label.element.style.display = 'flex'; - if (!element.children.length) { + const hasExtension = /\.[^/.]+$/.test(element.element.label); + if (!element.children.length && hasExtension) { templateData.label.setFile(element.element.uri, { fileKind: FileKind.FILE, hidePath: true, From 8f33e459f6af6c408655a1875c3475e71f45993f Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 31 Aug 2023 15:51:04 -0700 Subject: [PATCH 422/607] Only update layout when chat is visible (#191943) Fixes https://github.com/microsoft/vscode/issues/191942 --- src/vs/workbench/contrib/chat/browser/chatQuick.ts | 14 +++++++++++++- .../workbench/contrib/chat/browser/chatWidget.ts | 11 +++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 32592d223ab..5c41a1a6f7e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -134,6 +134,7 @@ class QuickChat extends Disposable { private model: ChatModel | undefined; private _currentQuery: string | undefined; private maintainScrollTimer: MutableDisposable = this._register(new MutableDisposable()); + private _deferUpdatingDynamicLayout: boolean = false; constructor( private readonly _options: IChatViewOptions, @@ -183,6 +184,10 @@ class QuickChat extends Disposable { this.widget.setVisible(true); // If the mutable disposable is set, then we are keeping the existing scroll position // so we should not update the layout. + if (this._deferUpdatingDynamicLayout) { + this._deferUpdatingDynamicLayout = false; + this.widget.updateDynamicChatTreeItemLayout(2, this.maxHeight); + } if (!this.maintainScrollTimer.value) { this.widget.layoutDynamicChatTreeItemMode(); } @@ -222,7 +227,14 @@ class QuickChat extends Disposable { private registerListeners(parent: HTMLElement): void { this._register(this.layoutService.onDidLayout(() => { - this.widget.updateDynamicChatTreeItemLayout(2, this.maxHeight); + if (this.widget.visible) { + this.widget.updateDynamicChatTreeItemLayout(2, this.maxHeight); + } else { + // If the chat is not visible, then we should defer updating the layout + // because it relies on offsetHeight which only works correctly + // when the chat is visible. + this._deferUpdatingDynamicLayout = true; + } })); this._register(this.widget.inputEditor.onDidChangeModelContent((e) => { this._currentQuery = this.widget.inputEditor.getValue(); diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index b37163a9990..91c15635f2e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -77,9 +77,12 @@ export class ChatWidget extends Disposable implements IChatWidget { private container!: HTMLElement; private bodyDimension: dom.Dimension | undefined; - private visible = false; private visibleChangeCount = 0; private requestInProgress: IContextKey; + private _visible = false; + public get visible() { + return this._visible; + } private previousTreeScrollHeight: number = 0; @@ -214,7 +217,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } private onDidChangeItems(skipDynamicLayout?: boolean) { - if (this.tree && this.visible) { + if (this.tree && this._visible) { const treeItems = (this.viewModel?.getItems() ?? []) .map(item => { return >{ @@ -261,7 +264,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } setVisible(visible: boolean): void { - this.visible = visible; + this._visible = visible; this.visibleChangeCount++; this.renderer.setVisible(visible); @@ -269,7 +272,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this._register(disposableTimeout(() => { // Progressive rendering paused while hidden, so start it up again. // Do it after a timeout because the container is not visible yet (it should be but offsetHeight returns 0 here) - if (this.visible) { + if (this._visible) { this.onDidChangeItems(true); } }, 0)); From ee08fd53cc990f4e8abaabf6e8014b3208128771 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Thu, 31 Aug 2023 15:52:13 -0700 Subject: [PATCH 423/607] Don't show chat widget context menu for filetree (#191940) --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 4bfefbd3af3..ebb17cc541f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -539,6 +539,10 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { this._onDidChangeItemHeight.fire({ element, height: templateData.rowContainer.offsetHeight }); })); + treeDisposables.add(tree.onContextMenu((e) => { + e.browserEvent.preventDefault(); + e.browserEvent.stopPropagation(); + })); tree.setInput(data).then(() => { if (!ref.isStale()) { From dd112ec0243c4b42bb3106e64df1688e242a6559 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Thu, 31 Aug 2023 16:37:56 -0700 Subject: [PATCH 424/607] Open walkthrough if a gettingStarted page is found (#191947) --- .../browser/gettingStarted.contribution.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts index a6da647dbcc..1e57d47d68a 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.contribution.ts @@ -66,10 +66,8 @@ registerAction2(class extends Action2 { // Try first to select the walkthrough on an active welcome page with no selected walkthrough for (const group of editorGroupsService.groups) { if (group.activeEditor instanceof GettingStartedInput) { - if (!group.activeEditor.selectedCategory) { - (group.activeEditorPane as GettingStartedPage).makeCategoryVisibleWhenAvailable(selectedCategory, selectedStep); - return; - } + (group.activeEditorPane as GettingStartedPage).makeCategoryVisibleWhenAvailable(selectedCategory, selectedStep); + return; } } @@ -106,7 +104,10 @@ registerAction2(class extends Action2 { editorService.openEditor({ resource: GettingStartedInput.RESOURCE, options: { selectedCategory: selectedCategory, selectedStep: selectedStep, preserveFocus: toSide ?? false } + }).then((editor) => { + (editor as GettingStartedPage)?.makeCategoryVisibleWhenAvailable(selectedCategory, selectedStep); }); + } } else { editorService.openEditor({ resource: GettingStartedInput.RESOURCE }); From 5f7b620db8ec603453798554a6596a7ad608fb3e Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 1 Sep 2023 15:32:05 +0900 Subject: [PATCH 425/607] chore: bump electron@25.8.0 (#191905) * chore: bump electron@25.8.0 * chore: update internal build id * chore: bump distro --- .yarnrc | 4 +-- build/checksums/electron.txt | 54 ++++++++++++++++++------------------ cgmanifest.json | 4 +-- package.json | 4 +-- yarn.lock | 8 +++--- 5 files changed, 37 insertions(+), 37 deletions(-) diff --git a/.yarnrc b/.yarnrc index 7b3fff4b526..fff0be195f2 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "25.7.0" -ms_build_id "23434598" +target "25.8.0" +ms_build_id "23503258" runtime "electron" build_from_source "true" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index a19497f08e8..9c46b2ad8ef 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,27 +1,27 @@ -efbcf77eb1a0783766f9579ffb9f9b68f04fea8cb091eab7ab8484ba0cd13fbf *electron-v25.7.0-darwin-arm64-symbols.zip -76a415165d212a345a5689de83078adc715fc10562bfaa35d7323094780ba683 *electron-v25.7.0-darwin-arm64.zip -07b9049848e877019d1dce71e06713125b605dda8ac5d0b8ab3aa899cf40551d *electron-v25.7.0-darwin-x64-symbols.zip -dea726ae9adc1c36206ce8d20ce32f630bcd684b869e0cb302f97c8bd26616d6 *electron-v25.7.0-darwin-x64.zip -b6c8ba123353984b2d3ffd6ccd52aec2d3238f71611c4c94bab75aa92804eebf *electron-v25.7.0-linux-arm64-symbols.zip -19e1e2c7ea1ab024f069e3dad6a26605e14b2c605e134484196343118fccf925 *electron-v25.7.0-linux-arm64.zip -ba0bbe84ea626c8064809c66487a3b77ad39bcf8b1daa0d9421428f78ad4d665 *electron-v25.7.0-linux-armv7l-symbols.zip -832a68cddb20eb847aca982b89f89e145f50dd483c71c8a705bbb9248fb7c665 *electron-v25.7.0-linux-armv7l.zip -2e616b446112533d3aa69ed1074ab1e0be5400996129aa636273d01462dc9506 *electron-v25.7.0-linux-x64-symbols.zip -002641e8103b77060e23b9c77c51ffb942372d01306210cdc3d32fc6ae5d112b *electron-v25.7.0-linux-x64.zip -162e0f7ca9fc1c17b8d84e9b9eccc65bb0f527a67f6339a19292d798085848e4 *electron-v25.7.0-win32-arm64-pdb.zip -7d98734ffcf10e1d002c30a212dd1f203b1418a295da67410490f83e9ced388c *electron-v25.7.0-win32-arm64-symbols.zip -9777d47f74d129f7c68ebffad640a6a527b83895c173c7d344f80fc9588bad85 *electron-v25.7.0-win32-arm64.zip -c805c6356378dccb21b5725004934534e187bdaf8149a6a457fdd60d243b41e4 *electron-v25.7.0-win32-ia32-pdb.zip -5f1a3b09153cf934f24f3b1853ad1788e7c27c6ddceb80e52fe07e2e69b6bb2b *electron-v25.7.0-win32-ia32-symbols.zip -fdf8e100c3d3cdb75b54ced1ecae96d6206eca08ebb07c5d8f08740e5e703509 *electron-v25.7.0-win32-ia32.zip -aa56314a675351e9457355f2cb0660c62a3be62cc340dad76fd216741064824d *electron-v25.7.0-win32-x64-pdb.zip -25d664dfe0823e1a12269feb6eb3886dba44b2d130b8787c4d58d3d0cbcf1c22 *electron-v25.7.0-win32-x64-symbols.zip -7ddb0b38207fd837cdf4e2b2778c365751315e321b09d346c8bb8476300d0ec0 *electron-v25.7.0-win32-x64.zip -02619733aadb13b6bf21df966e04775506d0d7595a0795003fed45631c4a0af6 *ffmpeg-v25.7.0-darwin-arm64.zip -69a8e2021e48f504021913c15633cbef2b4a7b28656c51cd238acdbf7c94e358 *ffmpeg-v25.7.0-darwin-x64.zip -bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.7.0-linux-arm64.zip -9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.7.0-linux-armv7l.zip -edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.7.0-linux-x64.zip -7076d4593f2e2e2abf0dc9ad8f6490d72b2fa89710def822f39da4363e49e504 *ffmpeg-v25.7.0-win32-arm64.zip -bd07183c1b6a93586d73c4106ceef0faae77f46763d15d6901d5954c2c5bba1b *ffmpeg-v25.7.0-win32-ia32.zip -b056e71a7c59441c551d5bbc1a8d99f2464a5809a3ba17d41540dc7174cab7b7 *ffmpeg-v25.7.0-win32-x64.zip +88cafda8394985e59d3d84cb4a6692ad04d8e32db9ecd6429e748e41526ddad7 *electron-v25.8.0-darwin-arm64-symbols.zip +6e33d3b8041561722ed41777e055a8c15d3f4e61b67367b2618918bcf0cfea76 *electron-v25.8.0-darwin-arm64.zip +438ac9915e062a239fb6d2595323c4783d2c820efc9cbcf3d2c1253d0e057e83 *electron-v25.8.0-darwin-x64-symbols.zip +798907d2a66bc79202c8213c61e7fd147ae2a8c31c485d814950b11d43bbbba8 *electron-v25.8.0-darwin-x64.zip +3243f3764319cff6c942d9f90a86323c36ec05ec51ef01e782c4e9a7194187e1 *electron-v25.8.0-linux-arm64-symbols.zip +f24f858b76bf8a2e18419f62e0f891712b2fa541089123e9caa8d5cd67fc3276 *electron-v25.8.0-linux-arm64.zip +dc3ff0489a0ebeda56d06b31eeae75dd7321a52bb601069c4475c56462b4814a *electron-v25.8.0-linux-armv7l-symbols.zip +3b7a0c3899f828a5cf30043b73992e90231400b90c1afa700a44f892a55e326b *electron-v25.8.0-linux-armv7l.zip +44803b2487406eca8fff9cec405e9e50bd92a911808dfaaa523b9ef52a0e72d8 *electron-v25.8.0-linux-x64-symbols.zip +d54fb2df0ad7318240220aa26327171ed1e891fb296f3c27c58b8b487c4df8eb *electron-v25.8.0-linux-x64.zip +bf7be6c0c8d0df06f0ce22e16c97aea823415d7f5cbf0ffdadf65d75feaf3cd8 *electron-v25.8.0-win32-arm64-pdb.zip +5d91757660b44bf30907f9c2b52225ade4d127d0fe48dc83dec134cc06c949f0 *electron-v25.8.0-win32-arm64-symbols.zip +d1e6f30a8d8c7aed28d08ddf915d79de6b16b3a0a7c84c45fd3cc0d47f2b7f53 *electron-v25.8.0-win32-arm64.zip +e389fef61c14ea0eefad91a9725aa0afd4dbdc982f7b30aba97bd9c2871c2061 *electron-v25.8.0-win32-ia32-pdb.zip +374d6c8897f97fab04e990ecf928e05f643ae33801546bf7d39bf4045b9d8b52 *electron-v25.8.0-win32-ia32-symbols.zip +73fc3382202b70dcaf7928f09a791662de82c701b8f403ed72cc5aa9b1401593 *electron-v25.8.0-win32-ia32.zip +010d248bd2e77585e1fa977e58b016659566de5a91c1e6845c85a7e6e1851bb9 *electron-v25.8.0-win32-x64-pdb.zip +72adb74fd92edff35c177c3c5d96765f230bc7adb8af11b30d5122b9e54c26e1 *electron-v25.8.0-win32-x64-symbols.zip +0051d0f241aedc6cdab4751c60f48758936122796f06c9e3033c7710a531686c *electron-v25.8.0-win32-x64.zip +2956915642c45eb0099228368d0af50e891e4c10014fa4d3d3bcfb135fbb89a7 *ffmpeg-v25.8.0-darwin-arm64.zip +099ee69d44f8ac3802cdd612895f279f7adb043a5b9c9d123479b0f96514a44c *ffmpeg-v25.8.0-darwin-x64.zip +bd52d57ff97fb56ac01a3482af905d04f0d4e9c13c53858c6d9f99957eca82da *ffmpeg-v25.8.0-linux-arm64.zip +9b3d09177fa1e63e2a6beecfa70aeec30aeb5c1873ff21128a68051c4e23f95d *ffmpeg-v25.8.0-linux-armv7l.zip +edc7b1c9f1a0733f109a2c0375a4e40c5bfe0bf28b7f06dcc76e1ada0aa2f125 *ffmpeg-v25.8.0-linux-x64.zip +a58e9480dab981ff973749e9d1e08936b2dd63a4b7f9523c030b1833387a4eb5 *ffmpeg-v25.8.0-win32-arm64.zip +6866b23a4d561c0322aeb7690aae646718c54398739946e352bf80d0dd721bfd *ffmpeg-v25.8.0-win32-ia32.zip +7b906df4ad6252881cf1e58619285b624f74d593379fbc6728e238b852d6abad *ffmpeg-v25.8.0-win32-x64.zip diff --git a/cgmanifest.json b/cgmanifest.json index df2f75f3209..6b2d2bfff44 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "f818ec3295c9688585e3cfea532ccc5b705746bb" + "commitHash": "84d7f7f071ae11637d4a41b95536410293672750" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "25.7.0" + "version": "25.8.0" }, { "component": { diff --git a/package.json b/package.json index 1a58592be0a..997a4931166 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.82.0", - "distro": "021e674d5265eb9125cfc0282c3a9a6091f4982d", + "distro": "0a5805caff2d59440704a3bf75eebaa509be862f", "author": { "name": "Microsoft Corporation" }, @@ -150,7 +150,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "25.7.0", + "electron": "25.8.0", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", diff --git a/yarn.lock b/yarn.lock index e171d0e0f55..aac325ba08f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3587,10 +3587,10 @@ electron-to-chromium@^1.4.202: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.207.tgz#9c3310ebace2952903d05dcaba8abe3a4ed44c01" integrity sha512-piH7MJDJp4rJCduWbVvmUd59AUne1AFBJ8JaRQvk0KzNTSUnZrVXHCZc+eg+CGE4OujkcLJznhGKD6tuAshj5Q== -electron@25.7.0: - version "25.7.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-25.7.0.tgz#0076c2e6acfe363f666a7b77d826a6f8a3028bcd" - integrity sha512-P82EzYZ8k9J21x5syhXV7EkezDmEXwycReXnagfzS0kwepnrlWzq1aDIUWdNvzTdHobky4m/nYcL98qd73mEVA== +electron@25.8.0: + version "25.8.0" + resolved "https://registry.yarnpkg.com/electron/-/electron-25.8.0.tgz#60c84f1f256924ac5a0aff13276b901b0c43767a" + integrity sha512-T3kC1a/3ntSaYMCVVfUUc9v7myPzi6J2GP0Ad/CyfWKDPp054dGyKxb2EEjKnxQQ7wfjsT1JTEdBG04x6ekVBw== dependencies: "@electron/get" "^2.0.0" "@types/node" "^18.11.18" From a5f4583b51a2d181a7495b7344d9ef90ee3fc2f8 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 1 Sep 2023 09:41:12 +0200 Subject: [PATCH 426/607] 1.83.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 997a4931166..dfd24be8535 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.82.0", + "version": "1.83.0", "distro": "0a5805caff2d59440704a3bf75eebaa509be862f", "author": { "name": "Microsoft Corporation" From 2ed3ef258820640f4bffbdd26c3393edc14910ac Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Sep 2023 10:32:59 +0200 Subject: [PATCH 427/607] editors - do not focus empty editor group when created (#191966) --- src/vs/workbench/browser/parts/editor/editorActions.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 149b76d8df9..e13b41e522f 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -2264,8 +2264,12 @@ abstract class AbstractCreateEditorGroupAction extends Action2 { override async run(accessor: ServicesAccessor): Promise { const editorGroupService = accessor.get(IEditorGroupsService); - const group = editorGroupService.addGroup(editorGroupService.activeGroup, this.direction, { activate: true }); - group.focus(); + // We intentionally do not want the new group to be focussed so that + // a user can have keyboard focus e.g. in a tree/list, open a new + // editor group that is active and then arrow-up/down in the tree/list + // to pick an editor to open in that group + + editorGroupService.addGroup(editorGroupService.activeGroup, this.direction, { activate: true }); } } From e4a24b361aae8b1fa2d6513b46c7fa10f90d30cd Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 1 Sep 2023 11:41:21 +0200 Subject: [PATCH 428/607] Enable the family autodetection algorithm (#191971) Fixes #191945: Enable the family autodetection algorithm to support a case where localhost resolves first to the ipv6 address and only second to the ipv4 address, and the desired server listens only on ipv4 --- src/vs/server/node/remoteExtensionHostAgentServer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 5a2d9943ac9..d1a0f51e783 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -551,7 +551,8 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { const socket = net.createConnection( { host: host, - port: port + port: port, + autoSelectFamily: true }, () => { socket.removeListener('error', e); socket.pause(); From 2c021905ad67daf18551e06661f86a76e2c64129 Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Fri, 1 Sep 2023 13:40:20 +0200 Subject: [PATCH 429/607] Do not repaint decorations overview ruler if it is not necessary (#191931) Fixes #191423: Do not repaint decorations overview ruler if it is not necessary --- .../overviewRuler/decorationsOverviewRuler.ts | 57 ++++++++++++++++--- src/vs/editor/common/viewModel.ts | 12 ++++ 2 files changed, 60 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 86db61f97e1..31e38a56c4d 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -17,6 +17,7 @@ import { EditorTheme } from 'vs/editor/common/editorTheme'; import * as viewEvents from 'vs/editor/common/viewEvents'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { OverviewRulerDecorationsGroup } from 'vs/editor/common/viewModel'; +import { equals } from 'vs/base/common/arrays'; class Settings { @@ -212,13 +213,24 @@ const enum OverviewRulerLane { Full = 7 } +const enum ShouldRenderValue { + NotNeeded = 0, + Maybe = 1, + Needed = 2 +} + export class DecorationsOverviewRuler extends ViewPart { + private _actualShouldRender: ShouldRenderValue = ShouldRenderValue.NotNeeded; + private readonly _tokensColorTrackerListener: IDisposable; private readonly _domNode: FastDomNode; private _settings!: Settings; private _cursorPositions: Position[]; + private _renderedDecorations: OverviewRulerDecorationsGroup[] = []; + private _renderedCursorPositions: Position[] = []; + constructor(context: ViewContext) { super(context); @@ -270,8 +282,18 @@ export class DecorationsOverviewRuler extends ViewPart { // ---- begin view event handlers + private _markRenderingIsNeeded(): true { + this._actualShouldRender = ShouldRenderValue.Needed; + return true; + } + + private _markRenderingIsMaybeNeeded(): true { + this._actualShouldRender = ShouldRenderValue.Maybe; + return true; + } + public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { - return this._updateSettings(false); + return this._updateSettings(false) ? this._markRenderingIsNeeded() : false; } public override onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { this._cursorPositions = []; @@ -279,25 +301,25 @@ export class DecorationsOverviewRuler extends ViewPart { this._cursorPositions[i] = e.selections[i].getPosition(); } this._cursorPositions.sort(Position.compare); - return true; + return this._markRenderingIsMaybeNeeded(); } public override onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { if (e.affectsOverviewRuler) { - return true; + return this._markRenderingIsMaybeNeeded(); } return false; } public override onFlushed(e: viewEvents.ViewFlushedEvent): boolean { - return true; + return this._markRenderingIsNeeded(); } public override onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { - return e.scrollHeightChanged; + return e.scrollHeightChanged ? this._markRenderingIsNeeded() : false; } public override onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { - return true; + return this._markRenderingIsNeeded(); } public override onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { - return this._updateSettings(false); + return this._updateSettings(false) ? this._markRenderingIsNeeded() : false; } // ---- end view event handlers @@ -312,6 +334,7 @@ export class DecorationsOverviewRuler extends ViewPart { public render(editorCtx: RestrictedRenderingContext): void { this._render(); + this._actualShouldRender = ShouldRenderValue.NotNeeded; } private _render(): void { @@ -322,6 +345,23 @@ export class DecorationsOverviewRuler extends ViewPart { this._domNode.setDisplay('none'); return; } + + const decorations = this._context.viewModel.getAllOverviewRulerDecorations(this._context.theme); + decorations.sort(OverviewRulerDecorationsGroup.cmp); + + if (this._actualShouldRender === ShouldRenderValue.Maybe && !OverviewRulerDecorationsGroup.equalsArr(this._renderedDecorations, decorations)) { + this._actualShouldRender = ShouldRenderValue.Needed; + } + if (this._actualShouldRender === ShouldRenderValue.Maybe && !equals(this._renderedCursorPositions, this._cursorPositions, (a, b) => a.lineNumber === b.lineNumber)) { + this._actualShouldRender = ShouldRenderValue.Needed; + } + if (this._actualShouldRender === ShouldRenderValue.Maybe) { + // both decorations and cursor positions are unchanged, nothing to do + return; + } + this._renderedDecorations = decorations; + this._renderedCursorPositions = this._cursorPositions; + this._domNode.setDisplay('block'); const canvasWidth = this._settings.canvasWidth; const canvasHeight = this._settings.canvasHeight; @@ -329,7 +369,6 @@ export class DecorationsOverviewRuler extends ViewPart { const viewLayout = this._context.viewLayout; const outerHeight = this._context.viewLayout.getScrollHeight(); const heightRatio = canvasHeight / outerHeight; - const decorations = this._context.viewModel.getAllOverviewRulerDecorations(this._context.theme); const minDecorationHeight = (Constants.MIN_DECORATION_HEIGHT * this._settings.pixelRatio) | 0; const halfMinDecorationHeight = (minDecorationHeight / 2) | 0; @@ -355,7 +394,7 @@ export class DecorationsOverviewRuler extends ViewPart { const x = this._settings.x; const w = this._settings.w; - decorations.sort(OverviewRulerDecorationsGroup.cmp); + for (const decorationGroup of decorations) { const color = decorationGroup.color; diff --git a/src/vs/editor/common/viewModel.ts b/src/vs/editor/common/viewModel.ts index e61ba01dc15..4e4b4d3032f 100644 --- a/src/vs/editor/common/viewModel.ts +++ b/src/vs/editor/common/viewModel.ts @@ -445,4 +445,16 @@ export class OverviewRulerDecorationsGroup { } return a.zIndex - b.zIndex; } + + public static equalsArr(a: OverviewRulerDecorationsGroup[], b: OverviewRulerDecorationsGroup[]): boolean { + if (a.length !== b.length) { + return false; + } + for (let i = 0, len = a.length; i < len; i++) { + if (OverviewRulerDecorationsGroup.cmp(a[i], b[i]) !== 0) { + return false; + } + } + return true; + } } From 02ddb145e8ae681362607bc76291fe9b8135c75b Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Fri, 1 Sep 2023 14:10:57 +0200 Subject: [PATCH 430/607] additing translation l10n --- extensions/json-language-features/server/src/jsonServer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/json-language-features/server/src/jsonServer.ts b/extensions/json-language-features/server/src/jsonServer.ts index 9b353d913ed..36ca0dc591d 100644 --- a/extensions/json-language-features/server/src/jsonServer.ts +++ b/extensions/json-language-features/server/src/jsonServer.ts @@ -14,6 +14,7 @@ import { DiagnosticsSupport, registerDiagnosticsPullSupport, registerDiagnostics import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, Range, Position, SortOptions } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; import { Utils, URI } from 'vscode-uri'; +import * as l10n from '@vscode/l10n'; type ISchemaAssociations = Record; @@ -432,7 +433,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) const sortCodeAction = CodeAction.create('Sort JSON', CodeActionKind.Source.concat('.sort', '.json')); sortCodeAction.command = { command: 'json.sort', - title: 'Sort JSON' + title: l10n.t('Sort JSON') }; return [sortCodeAction]; } From 415bc174ea00a7bbda4ca709e8dd86dd36a16336 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 1 Sep 2023 14:23:16 +0200 Subject: [PATCH 431/607] refine lint config, add missing jsdoc and jsdoc-tag corrections --- .eslintrc.json | 10 +- src/vscode-dts/vscode.d.ts | 1435 ++++++++++++++++++++++++++++-------- 2 files changed, 1141 insertions(+), 304 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index b2c303da84a..f44673cd1cd 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -201,9 +201,6 @@ "**/vscode.d.ts" ], "rules": { - "extends": [ - "plugin:jsdoc/recommended-typescript" - ], "jsdoc/tag-lines": "off", "jsdoc/valid-types": "off", "jsdoc/no-multi-asterisks": [ @@ -220,6 +217,7 @@ "TSInterfaceDeclaration", "TSPropertySignature", "TSMethodSignature", + "TSDeclareFunction", "ClassDeclaration", "MethodDefinition", "PropertyDeclaration", @@ -232,9 +230,11 @@ "jsdoc/check-param-names": [ "warn", { - "enableFixer": false + "enableFixer": false, + "checkDestructured": false } - ] + ], + "jsdoc/require-returns": "warn" } }, { diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 4f5c61daaa7..97f52cb344d 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -136,7 +136,7 @@ declare module 'vscode' { /** * Save the underlying file. * - * @return A promise that will resolve to `true` when the file + * @returns A promise that will resolve to `true` when the file * has been saved. If the save failed, will return `false`. */ save(): Thenable; @@ -158,7 +158,7 @@ declare module 'vscode' { * document are not reflected. * * @param line A line number in [0, lineCount). - * @return A {@link TextLine line}. + * @returns A {@link TextLine line}. */ lineAt(line: number): TextLine; @@ -172,7 +172,7 @@ declare module 'vscode' { * @see {@link TextDocument.lineAt} * * @param position A position. - * @return A {@link TextLine line}. + * @returns A {@link TextLine line}. */ lineAt(position: Position): TextLine; @@ -182,7 +182,7 @@ declare module 'vscode' { * The position will be {@link TextDocument.validatePosition adjusted}. * * @param position A position. - * @return A valid zero-based offset. + * @returns A valid zero-based offset. */ offsetAt(position: Position): number; @@ -190,7 +190,7 @@ declare module 'vscode' { * Converts a zero-based offset to a position. * * @param offset A zero-based offset. - * @return A valid {@link Position}. + * @returns A valid {@link Position}. */ positionAt(offset: number): Position; @@ -199,7 +199,7 @@ declare module 'vscode' { * a range. The range will be {@link TextDocument.validateRange adjusted}. * * @param range Include only the text included by the range. - * @return The text inside the provided range or the entire text. + * @returns The text inside the provided range or the entire text. */ getText(range?: Range): string; @@ -219,7 +219,7 @@ declare module 'vscode' { * * @param position A position. * @param regex Optional regular expression that describes what a word is. - * @return A range spanning a word, or `undefined`. + * @returns A range spanning a word, or `undefined`. */ getWordRangeAtPosition(position: Position, regex?: RegExp): Range | undefined; @@ -227,7 +227,7 @@ declare module 'vscode' { * Ensure a range is completely contained in this document. * * @param range A range. - * @return The given range or a new, adjusted range. + * @returns The given range or a new, adjusted range. */ validateRange(range: Range): Range; @@ -235,7 +235,7 @@ declare module 'vscode' { * Ensure a position is contained in the range of this document. * * @param position A position. - * @return The given position or a new, adjusted position. + * @returns The given position or a new, adjusted position. */ validatePosition(position: Position): Position; } @@ -270,7 +270,7 @@ declare module 'vscode' { * Check if this position is before `other`. * * @param other A position. - * @return `true` if position is on a smaller line + * @returns `true` if position is on a smaller line * or on the same line on a smaller character. */ isBefore(other: Position): boolean; @@ -279,7 +279,7 @@ declare module 'vscode' { * Check if this position is before or equal to `other`. * * @param other A position. - * @return `true` if position is on a smaller line + * @returns `true` if position is on a smaller line * or on the same line on a smaller or equal character. */ isBeforeOrEqual(other: Position): boolean; @@ -288,7 +288,7 @@ declare module 'vscode' { * Check if this position is after `other`. * * @param other A position. - * @return `true` if position is on a greater line + * @returns `true` if position is on a greater line * or on the same line on a greater character. */ isAfter(other: Position): boolean; @@ -297,7 +297,7 @@ declare module 'vscode' { * Check if this position is after or equal to `other`. * * @param other A position. - * @return `true` if position is on a greater line + * @returns `true` if position is on a greater line * or on the same line on a greater or equal character. */ isAfterOrEqual(other: Position): boolean; @@ -306,7 +306,7 @@ declare module 'vscode' { * Check if this position is equal to `other`. * * @param other A position. - * @return `true` if the line and character of the given position are equal to + * @returns `true` if the line and character of the given position are equal to * the line and character of this position. */ isEqual(other: Position): boolean; @@ -315,7 +315,7 @@ declare module 'vscode' { * Compare this to `other`. * * @param other A position. - * @return A number smaller than zero if this position is before the given position, + * @returns A number smaller than zero if this position is before the given position, * a number greater than zero if this position is after the given position, or zero when * this and the given position are equal. */ @@ -326,7 +326,7 @@ declare module 'vscode' { * * @param lineDelta Delta value for the line value, default is `0`. * @param characterDelta Delta value for the character value, default is `0`. - * @return A position which line and character is the sum of the current line and + * @returns A position which line and character is the sum of the current line and * character and the corresponding deltas. */ translate(lineDelta?: number, characterDelta?: number): Position; @@ -335,17 +335,26 @@ declare module 'vscode' { * Derived a new position relative to this position. * * @param change An object that describes a delta to this position. - * @return A position that reflects the given delta. Will return `this` position if the change + * @returns A position that reflects the given delta. Will return `this` position if the change * is not changing anything. */ - translate(change: { lineDelta?: number; characterDelta?: number }): Position; + translate(change: { + /** + * Delta value for the line value, default is `0`. + */ + lineDelta?: number; + /** + * Delta value for the character value, default is `0`. + */ + characterDelta?: number; + }): Position; /** * Create a new position derived from this position. * * @param line Value that should be used as line value, default is the {@link Position.line existing value} * @param character Value that should be used as character value, default is the {@link Position.character existing value} - * @return A position where line and character are replaced by the given values. + * @returns A position where line and character are replaced by the given values. */ with(line?: number, character?: number): Position; @@ -353,10 +362,19 @@ declare module 'vscode' { * Derived a new position from this position. * * @param change An object that describes a change to this position. - * @return A position that reflects the given change. Will return `this` position if the change + * @returns A position that reflects the given change. Will return `this` position if the change * is not changing anything. */ - with(change: { line?: number; character?: number }): Position; + with(change: { + /** + * New line value, defaults the line value of `this`. + */ + line?: number; + /** + * New character value, defaults the character value of `this`. + */ + character?: number; + }): Position; } /** @@ -413,7 +431,7 @@ declare module 'vscode' { * Check if a position or a range is contained in this range. * * @param positionOrRange A position or a range. - * @return `true` if the position or range is inside or equal + * @returns `true` if the position or range is inside or equal * to this range. */ contains(positionOrRange: Position | Range): boolean; @@ -422,7 +440,7 @@ declare module 'vscode' { * Check if `other` equals this range. * * @param other A range. - * @return `true` when start and end are {@link Position.isEqual equal} to + * @returns `true` when start and end are {@link Position.isEqual equal} to * start and end of this range. */ isEqual(other: Range): boolean; @@ -432,7 +450,7 @@ declare module 'vscode' { * if the ranges have no overlap. * * @param range A range. - * @return A range of the greater start and smaller end positions. Will + * @returns A range of the greater start and smaller end positions. Will * return undefined when there is no overlap. */ intersection(range: Range): Range | undefined; @@ -441,7 +459,7 @@ declare module 'vscode' { * Compute the union of `other` with this range. * * @param other A range. - * @return A range of smaller start position and the greater end position. + * @returns A range of smaller start position and the greater end position. */ union(other: Range): Range; @@ -450,7 +468,7 @@ declare module 'vscode' { * * @param start A position that should be used as start. The default value is the {@link Range.start current start}. * @param end A position that should be used as end. The default value is the {@link Range.end current end}. - * @return A range derived from this range with the given start and end position. + * @returns A range derived from this range with the given start and end position. * If start and end are not different `this` range will be returned. */ with(start?: Position, end?: Position): Range; @@ -459,10 +477,19 @@ declare module 'vscode' { * Derived a new range from this range. * * @param change An object that describes a change to this range. - * @return A range that reflects the given change. Will return `this` range if the change + * @returns A range that reflects the given change. Will return `this` range if the change * is not changing anything. */ - with(change: { start?: Position; end?: Position }): Range; + with(change: { + /** + * New start position, defaults to {@link Range.start current start} + */ + start?: Position; + /** + * New end position, defaults to {@link Range.end current end} + */ + end?: Position; + }): Range; } /** @@ -718,9 +745,21 @@ declare module 'vscode' { * The overview ruler supports three lanes. */ export enum OverviewRulerLane { + /** + * The left lane of the overview ruler. + */ Left = 1, + /** + * The center lane of the overview ruler. + */ Center = 2, + /** + * The right lane of the overview ruler. + */ Right = 4, + /** + * All lanes of the overview ruler. + */ Full = 7 } @@ -1020,6 +1059,10 @@ declare module 'vscode' { after?: ThemableDecorationAttachmentRenderOptions; } + /** + * Represents theme specific rendeirng styles for {@link ThemableDecorationRenderOptions.before before} and + * {@link ThemableDecorationRenderOptions.after after} the content of text decorations. + */ export interface ThemableDecorationAttachmentRenderOptions { /** * Defines a text content that is shown in the attachment. Either an icon or a text can be shown, but not both. @@ -1126,6 +1169,9 @@ declare module 'vscode' { renderOptions?: DecorationInstanceRenderOptions; } + /** + * Represents themable render options for decoration instances. + */ export interface ThemableDecorationInstanceRenderOptions { /** * Defines the rendering options of the attachment that is inserted before the decorated text. @@ -1138,6 +1184,9 @@ declare module 'vscode' { after?: ThemableDecorationAttachmentRenderOptions; } + /** + * Represents render options for decoration instances. See {@link DecorationOptions.renderOptions}. + */ export interface DecorationInstanceRenderOptions extends ThemableDecorationInstanceRenderOptions { /** * Overwrite options for light themes. @@ -1197,9 +1246,18 @@ declare module 'vscode' { * * @param callback A function which can create edits using an {@link TextEditorEdit edit-builder}. * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. - * @return A promise that resolves with a value indicating if the edits could be applied. + * @returns A promise that resolves with a value indicating if the edits could be applied. */ - edit(callback: (editBuilder: TextEditorEdit) => void, options?: { readonly undoStopBefore: boolean; readonly undoStopAfter: boolean }): Thenable; + edit(callback: (editBuilder: TextEditorEdit) => void, options?: { + /** + * Add undo stop before making the edits. + */ + readonly undoStopBefore: boolean; + /** + * Add undo stop after making the edits. + */ + readonly undoStopAfter: boolean; + }): Thenable; /** * Insert a {@link SnippetString snippet} and put the editor into snippet mode. "Snippet mode" @@ -1209,10 +1267,19 @@ declare module 'vscode' { * @param snippet The snippet to insert in this edit. * @param location Position or range at which to insert the snippet, defaults to the current editor selection or selections. * @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit. - * @return A promise that resolves with a value indicating if the snippet could be inserted. Note that the promise does not signal + * @returns A promise that resolves with a value indicating if the snippet could be inserted. Note that the promise does not signal * that the snippet is completely filled-in or accepted. */ - insertSnippet(snippet: SnippetString, location?: Position | Range | readonly Position[] | readonly Range[], options?: { readonly undoStopBefore: boolean; readonly undoStopAfter: boolean }): Thenable; + insertSnippet(snippet: SnippetString, location?: Position | Range | readonly Position[] | readonly Range[], options?: { + /** + * Add undo stop before making the edits. + */ + readonly undoStopBefore: boolean; + /** + * Add undo stop after making the edits. + */ + readonly undoStopAfter: boolean; + }): Thenable; /** * Adds a set of decorations to the text editor. If a set of decorations already exists with @@ -1325,7 +1392,7 @@ declare module 'vscode' { * @see {@link Uri.toString} * @param value The string value of an Uri. * @param strict Throw an error when `value` is empty or when no `scheme` can be parsed. - * @return A new Uri instance. + * @returns A new Uri instance. */ static parse(value: string, strict?: boolean): Uri; @@ -1350,7 +1417,7 @@ declare module 'vscode' { * ``` * * @param path A file system or UNC path. - * @return A new Uri instance. + * @returns A new Uri instance. */ static file(path: string): Uri; @@ -1381,9 +1448,30 @@ declare module 'vscode' { * * @see {@link Uri.toString} * @param components The component parts of an Uri. - * @return A new Uri instance. + * @returns A new Uri instance. */ - static from(components: { readonly scheme: string; readonly authority?: string; readonly path?: string; readonly query?: string; readonly fragment?: string }): Uri; + static from(components: { + /** + * The scheme of the uri + */ + readonly scheme: string; + /** + * The authority of the uri + */ + readonly authority?: string; + /** + * The path of the uri + */ + readonly path?: string; + /** + * The query string of the uri + */ + readonly query?: string; + /** + * The fragment identifier of the uri + */ + readonly fragment?: string; + }): Uri; /** * Use the `file` and `parse` factory functions to create new `Uri` objects. @@ -1450,10 +1538,31 @@ declare module 'vscode' { * * @param change An object that describes a change to this Uri. To unset components use `null` or * the empty string. - * @return A new Uri that reflects the given change. Will return `this` Uri if the change + * @returns A new Uri that reflects the given change. Will return `this` Uri if the change * is not changing anything. */ - with(change: { scheme?: string; authority?: string; path?: string; query?: string; fragment?: string }): Uri; + with(change: { + /** + * The new scheme, defauls to this Uri's scheme. + */ + scheme?: string; + /** + * The new authority, defaults to this Uri's authority. + */ + authority?: string; + /** + * The new path, defaults to this Uri's path. + */ + path?: string; + /** + * The new query, defaults to this Uri's query. + */ + query?: string; + /** + * The new fragment, defaults to this Uri's fragment. + */ + fragment?: string; + }): Uri; /** * Returns a string representation of this Uri. The representation and normalization @@ -1477,7 +1586,7 @@ declare module 'vscode' { /** * Returns a JSON representation of this Uri. * - * @return An object. + * @returns An object. */ toJSON(): any; } @@ -1551,10 +1660,15 @@ declare module 'vscode' { * * @param disposableLikes Objects that have at least a `dispose`-function member. Note that asynchronous * dispose-functions aren't awaited. - * @return Returns a new disposable which, upon dispose, will + * @returns Returns a new disposable which, upon dispose, will * dispose all provided disposables. */ - static from(...disposableLikes: { dispose: () => any }[]): Disposable; + static from(...disposableLikes: { + /** + * Function to clean up resources. + */ + dispose: () => any; + }[]): Disposable; /** * Creates a new disposable that calls the provided function @@ -1590,7 +1704,7 @@ declare module 'vscode' { * @param listener The listener function will be called when the event happens. * @param thisArgs The `this`-argument which will be used when calling the event listener. * @param disposables An array to which a {@link Disposable} will be added. - * @return A disposable which unsubscribes the event listener. + * @returns A disposable which unsubscribes the event listener. */ (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]): Disposable; } @@ -1695,7 +1809,7 @@ declare module 'vscode' { * * @param uri An uri which scheme matches the scheme this provider was {@link workspace.registerTextDocumentContentProvider registered} for. * @param token A cancellation token. - * @return A string or a thenable that resolves to such. + * @returns A string or a thenable that resolves to such. */ provideTextDocumentContent(uri: Uri, token: CancellationToken): ProviderResult; } @@ -1736,7 +1850,16 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the QuickPickItem. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; /** * A human-readable string which is rendered less prominent in the same line. Supports rendering of @@ -1983,9 +2106,21 @@ declare module 'vscode' { /** * Impacts the behavior and appearance of the validation message. */ + /** + * The severity level for input box validation. + */ export enum InputBoxValidationSeverity { + /** + * Informational severity level. + */ Info = 1, + /** + * Warning severity level. + */ Warning = 2, + /** + * Error severity level. + */ Error = 3 } @@ -2055,7 +2190,7 @@ declare module 'vscode' { * to the user. * * @param value The current value of the input box. - * @return Either a human-readable string which is presented as an error message or an {@link InputBoxValidationMessage} + * @returns Either a human-readable string which is presented as an error message or an {@link InputBoxValidationMessage} * which can provide a specific message severity. Return `undefined`, `null`, or the empty string when 'value' is valid. */ validateInput?(value: string): string | InputBoxValidationMessage | undefined | null | @@ -2330,6 +2465,11 @@ declare module 'vscode' { */ static readonly SourceFixAll: CodeActionKind; + /** + * Private constructor, use statix `CodeActionKind.XYZ` to derive from an existing code action kind. + * + * @param value The value of the kind, such as `refactor.extract.function`. + */ private constructor(value: string); /** @@ -2516,7 +2656,7 @@ declare module 'vscode' { * actions and avoid returning irrelevant code actions that the editor will discard. * @param token A cancellation token. * - * @return An array of code actions, such as quick fixes or refactorings. The lack of a result can be signaled + * @returns An array of code actions, such as quick fixes or refactorings. The lack of a result can be signaled * by returning `undefined`, `null`, or an empty array. * * We also support returning `Command` for legacy reasons, however all new extensions should return @@ -2535,7 +2675,7 @@ declare module 'vscode' { * * @param codeAction A code action. * @param token A cancellation token. - * @return The resolved code action or a thenable that resolves to such. It is OK to return the given + * @returns The resolved code action or a thenable that resolves to such. It is OK to return the given * `item`. When no result is returned, the given `item` will be used. */ resolveCodeAction?(codeAction: T, token: CancellationToken): ProviderResult; @@ -2644,7 +2784,7 @@ declare module 'vscode' { * * @param document The document in which the command was invoked. * @param token A cancellation token. - * @return An array of code lenses or a thenable that resolves to such. The lack of a result can be + * @returns An array of code lenses or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult; @@ -2655,7 +2795,7 @@ declare module 'vscode' { * * @param codeLens Code lens that must be resolved. * @param token A cancellation token. - * @return The given, resolved code lens or thenable that resolves to such. + * @returns The given, resolved code lens or thenable that resolves to such. */ resolveCodeLens?(codeLens: T, token: CancellationToken): ProviderResult; } @@ -2688,7 +2828,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. - * @return A definition or a thenable that resolves to such. The lack of a result can be + * @returns A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; @@ -2706,7 +2846,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. - * @return A definition or a thenable that resolves to such. The lack of a result can be + * @returns A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideImplementation(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; @@ -2724,7 +2864,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. - * @return A definition or a thenable that resolves to such. The lack of a result can be + * @returns A definition or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideTypeDefinition(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; @@ -2748,7 +2888,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. - * @return A declaration or a thenable that resolves to such. The lack of a result can be + * @returns A declaration or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideDeclaration(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; @@ -2774,10 +2914,13 @@ declare module 'vscode' { * markdown supports links that execute commands, e.g. `[Run it](command:myCommandId)`. * * Defaults to `false` (commands are disabled). - * - * If this is an object, only the set of commands listed in `enabledCommands` are allowed. */ - isTrusted?: boolean | { readonly enabledCommands: readonly string[] }; + isTrusted?: boolean | { + /** + * A set of commend ids that are allowed to be executed by this markdown string. + */ + readonly enabledCommands: readonly string[]; + }; /** * Indicates that this markdown string can contain {@link ThemeIcon ThemeIcons}, e.g. `$(zap)`. @@ -2852,7 +2995,18 @@ declare module 'vscode' { * * @deprecated This type is deprecated, please use {@linkcode MarkdownString} instead. */ - export type MarkedString = string | { language: string; value: string }; + export type MarkedString = string | { + /** + * The language of a markdown code block + * @deprecated, please use {@linkcode MarkdownString} instead + */ + language: string; + /** + * The code snippet of a markdown code block. + * @deprecated, please use {@linkcode MarkdownString} instead + */ + value: string; + }; /** * A hover represents additional information for a symbol or word. Hovers are @@ -2895,7 +3049,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. - * @return A hover or a thenable that resolves to such. The lack of a result can be + * @returns A hover or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideHover(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; @@ -2944,7 +3098,7 @@ declare module 'vscode' { * @param document The document for which the debug hover is about to appear. * @param position The line and character position in the document where the debug hover is about to appear. * @param token A cancellation token. - * @return An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be + * @returns An EvaluatableExpression or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideEvaluatableExpression(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; @@ -3072,7 +3226,7 @@ declare module 'vscode' { * @param viewPort The visible document range for which inline values should be computed. * @param context A bag containing contextual information like the current location. * @param token A cancellation token. - * @return An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be + * @returns An array of InlineValueDescriptors or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideInlineValues(document: TextDocument, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; @@ -3138,7 +3292,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param position The position at which the command was invoked. * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be + * @returns An array of document highlights or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentHighlights(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; @@ -3148,31 +3302,109 @@ declare module 'vscode' { * A symbol kind. */ export enum SymbolKind { + /** + * The `File` symbol kind. + */ File = 0, + /** + * The `Module` symbol kind. + */ Module = 1, + /** + * The `Namespace` symbol kind. + */ Namespace = 2, + /** + * The `Package` symbol kind. + */ Package = 3, + /** + * The `Class` symbol kind. + */ Class = 4, + /** + * The `Method` symbol kind. + */ Method = 5, + /** + * The `Property` symbol kind. + */ Property = 6, + /** + * The `Field` symbol kind. + */ Field = 7, + /** + * The `Constructor` symbol kind. + */ Constructor = 8, + /** + * The `Enum` symbol kind. + */ Enum = 9, + /** + * The `Interface` symbol kind. + */ Interface = 10, + /** + * The `Function` symbol kind. + */ Function = 11, + /** + * The `Variable` symbol kind. + */ Variable = 12, + /** + * The `Constant` symbol kind. + */ Constant = 13, + /** + * The `String` symbol kind. + */ String = 14, + /** + * The `Number` symbol kind. + */ Number = 15, + /** + * The `Boolean` symbol kind. + */ Boolean = 16, + /** + * The `Array` symbol kind. + */ Array = 17, + /** + * The `Object` symbol kind. + */ Object = 18, + /** + * The `Key` symbol kind. + */ Key = 19, + /** + * The `Null` symbol kind. + */ Null = 20, + /** + * The `EnumMember` symbol kind. + */ EnumMember = 21, + /** + * The `Struct` symbol kind. + */ Struct = 22, + /** + * The `Event` symbol kind. + */ Event = 23, + /** + * The `Operator` symbol kind. + */ Operator = 24, + /** + * The `TypeParameter` symbol kind. + */ TypeParameter = 25 } @@ -3308,7 +3540,7 @@ declare module 'vscode' { * * @param document The document in which the command was invoked. * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be + * @returns An array of document highlights or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentSymbols(document: TextDocument, token: CancellationToken): ProviderResult; @@ -3344,7 +3576,7 @@ declare module 'vscode' { * * @param query A query string, can be the empty string in which case all symbols should be returned. * @param token A cancellation token. - * @return An array of document highlights or a thenable that resolves to such. The lack of a result can be + * @returns An array of document highlights or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideWorkspaceSymbols(query: string, token: CancellationToken): ProviderResult; @@ -3358,7 +3590,7 @@ declare module 'vscode' { * @param symbol The symbol that is to be resolved. Guaranteed to be an instance of an object returned from an * earlier call to `provideWorkspaceSymbols`. * @param token A cancellation token. - * @return The resolved symbol or a thenable that resolves to that. When no result is returned, + * @returns The resolved symbol or a thenable that resolves to that. When no result is returned, * the given `symbol` is used. */ resolveWorkspaceSymbol?(symbol: T, token: CancellationToken): ProviderResult; @@ -3389,7 +3621,7 @@ declare module 'vscode' { * @param position The position at which the command was invoked. * @param token A cancellation token. * - * @return An array of locations or a thenable that resolves to such. The lack of a result can be + * @returns An array of locations or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideReferences(document: TextDocument, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult; @@ -3406,7 +3638,7 @@ declare module 'vscode' { * * @param range A range. * @param newText A string. - * @return A new text edit object. + * @returns A new text edit object. */ static replace(range: Range, newText: string): TextEdit; @@ -3415,7 +3647,7 @@ declare module 'vscode' { * * @param position A position, will become an empty range. * @param newText A string. - * @return A new text edit object. + * @returns A new text edit object. */ static insert(position: Position, newText: string): TextEdit; @@ -3423,7 +3655,7 @@ declare module 'vscode' { * Utility to create a delete edit. * * @param range A range. - * @return A new text edit object. + * @returns A new text edit object. */ static delete(range: Range): TextEdit; @@ -3431,7 +3663,7 @@ declare module 'vscode' { * Utility to create an eol-edit. * * @param eol An eol-sequence - * @return A new text edit object. + * @returns A new text edit object. */ static setEndOfLine(eol: EndOfLine): TextEdit; @@ -3478,7 +3710,7 @@ declare module 'vscode' { * * @param range A range. * @param snippet A snippet string. - * @return A new snippet edit object. + * @returns A new snippet edit object. */ static replace(range: Range, snippet: SnippetString): SnippetTextEdit; @@ -3487,7 +3719,7 @@ declare module 'vscode' { * * @param position A position, will become an empty range. * @param snippet A snippet string. - * @return A new snippet edit object. + * @returns A new snippet edit object. */ static insert(position: Position, snippet: SnippetString): SnippetTextEdit; @@ -3573,6 +3805,12 @@ declare module 'vscode' { */ newNotebookMetadata?: { [key: string]: any }; + /** + * Create a new notebook edit. + * + * @param range A notebook range. + * @param newCells An array of new cell data. + */ constructor(range: NotebookRange, newCells: NotebookCellData[]); } @@ -3601,7 +3839,16 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the edit. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; } /** @@ -3660,7 +3907,7 @@ declare module 'vscode' { * Check if a text edit for a resource exists. * * @param uri A resource identifier. - * @return `true` if the given resource will be touched by this edit. + * @returns `true` if the given resource will be touched by this edit. */ has(uri: Uri): boolean; @@ -3700,7 +3947,7 @@ declare module 'vscode' { * Get the text edits for a resource. * * @param uri A resource identifier. - * @return An array of text edits. + * @returns An array of text edits. */ get(uri: Uri): TextEdit[]; @@ -3716,9 +3963,14 @@ declare module 'vscode' { * @param metadata Optional metadata for the entry. */ createFile(uri: Uri, options?: { + /** + * Overwrite existing file. Overwrite wins over `ignoreIfExists` + */ readonly overwrite?: boolean; + /** + * Do nothing if a file with `uri` exists already. + */ readonly ignoreIfExists?: boolean; - /** * The initial contents of the new file. * @@ -3734,7 +3986,16 @@ declare module 'vscode' { * @param uri The uri of the file that is to be deleted. * @param metadata Optional metadata for the entry. */ - deleteFile(uri: Uri, options?: { readonly recursive?: boolean; readonly ignoreIfNotExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; + deleteFile(uri: Uri, options?: { + /** + * Delete the content recursively if a folder is denoted. + */ + readonly recursive?: boolean; + /** + * Do nothing if a file with `uri` exists already. + */ + readonly ignoreIfNotExists?: boolean; + }, metadata?: WorkspaceEditEntryMetadata): void; /** * Rename a file or folder. @@ -3745,12 +4006,21 @@ declare module 'vscode' { * ignored. When overwrite and ignoreIfExists are both set overwrite wins. * @param metadata Optional metadata for the entry. */ - renameFile(oldUri: Uri, newUri: Uri, options?: { readonly overwrite?: boolean; readonly ignoreIfExists?: boolean }, metadata?: WorkspaceEditEntryMetadata): void; + renameFile(oldUri: Uri, newUri: Uri, options?: { + /** + * Overwrite existing file. Overwrite wins over `ignoreIfExists` + */ + readonly overwrite?: boolean; + /** + * Do nothing if a file with `uri` exists already. + */ + readonly ignoreIfExists?: boolean; + }, metadata?: WorkspaceEditEntryMetadata): void; /** * Get all text edits grouped by resource. * - * @return A shallow copy of `[Uri, TextEdit[]]`-tuples. + * @returns A shallow copy of `[Uri, TextEdit[]]`-tuples. */ entries(): [Uri, TextEdit[]][]; } @@ -3772,6 +4042,11 @@ declare module 'vscode' { */ value: string; + /** + * Create a new snippet string. + * + * @param value A snippet string. + */ constructor(value?: string); /** @@ -3779,7 +4054,7 @@ declare module 'vscode' { * the {@linkcode SnippetString.value value} of this snippet string. * * @param string A value to append 'as given'. The string will be escaped. - * @return This snippet string. + * @returns This snippet string. */ appendText(string: string): SnippetString; @@ -3789,7 +4064,7 @@ declare module 'vscode' { * * @param number The number of this tabstop, defaults to an auto-increment * value starting at 1. - * @return This snippet string. + * @returns This snippet string. */ appendTabstop(number?: number): SnippetString; @@ -3801,7 +4076,7 @@ declare module 'vscode' { * with which a nested snippet can be created. * @param number The number of this tabstop, defaults to an auto-increment * value starting at 1. - * @return This snippet string. + * @returns This snippet string. */ appendPlaceholder(value: string | ((snippet: SnippetString) => any), number?: number): SnippetString; @@ -3812,7 +4087,7 @@ declare module 'vscode' { * @param values The values for choices - the array of strings * @param number The number of this tabstop, defaults to an auto-increment * value starting at 1. - * @return This snippet string. + * @returns This snippet string. */ appendChoice(values: readonly string[], number?: number): SnippetString; @@ -3823,7 +4098,7 @@ declare module 'vscode' { * @param name The name of the variable - excluding the `$`. * @param defaultValue The default value which is used when the variable name cannot * be resolved - either a string or a function with which a nested snippet can be created. - * @return This snippet string. + * @returns This snippet string. */ appendVariable(name: string, defaultValue: string | ((snippet: SnippetString) => any)): SnippetString; } @@ -3842,7 +4117,7 @@ declare module 'vscode' { * @param position The position at which the command was invoked. * @param newName The new name of the symbol. If the given name is not valid, the provider must return a rejected promise. * @param token A cancellation token. - * @return A workspace edit or a thenable that resolves to such. The lack of a result can be + * @returns A workspace edit or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideRenameEdits(document: TextDocument, position: Position, newName: string, token: CancellationToken): ProviderResult; @@ -3858,9 +4133,18 @@ declare module 'vscode' { * @param document The document in which rename will be invoked. * @param position The position at which rename will be invoked. * @param token A cancellation token. - * @return The range or range and placeholder text of the identifier that is to be renamed. The lack of a result can signaled by returning `undefined` or `null`. + * @returns The range or range and placeholder text of the identifier that is to be renamed. The lack of a result can signaled by returning `undefined` or `null`. */ - prepareRename?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; + prepareRename?(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } /** @@ -3877,6 +4161,12 @@ declare module 'vscode' { */ readonly tokenModifiers: string[]; + /** + * Creates a semantic tokens legend. + * + * @param tokenTypes An array of token types. + * @param tokenModifiers An array of token modifiers. + */ constructor(tokenTypes: string[], tokenModifiers?: string[]); } @@ -3886,6 +4176,11 @@ declare module 'vscode' { */ export class SemanticTokensBuilder { + /** + * Creates a semantic tokens builder. + * + * @param legend A semantic tokens legent. + */ constructor(legend?: SemanticTokensLegend); /** @@ -3932,6 +4227,12 @@ declare module 'vscode' { */ readonly data: Uint32Array; + /** + * Create new semantic tokens. + * + * @param data Token data. + * @param resultId Result identifier. + */ constructor(data: Uint32Array, resultId?: string); } @@ -3952,6 +4253,12 @@ declare module 'vscode' { */ readonly edits: SemanticTokensEdit[]; + /** + * Create new semantic tokens edits. + * + * @param edits An array of semantic token edits + * @param resultId Result identifier. + */ constructor(edits: SemanticTokensEdit[], resultId?: string); } @@ -3973,6 +4280,13 @@ declare module 'vscode' { */ readonly data: Uint32Array | undefined; + /** + * Create a semantic token edit. + * + * @param start Start offset + * @param deleteCount Number of elements to remove. + * @param data Elements to insert + */ constructor(start: number, deleteCount: number, data?: Uint32Array); } @@ -4123,7 +4437,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param options Options controlling formatting. * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentFormattingEdits(document: TextDocument, options: FormattingOptions, token: CancellationToken): ProviderResult; @@ -4146,7 +4460,7 @@ declare module 'vscode' { * @param range The range which should be formatted. * @param options Options controlling formatting. * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult; @@ -4166,7 +4480,7 @@ declare module 'vscode' { * @param ranges The ranges which should be formatted. * @param options Options controlling formatting. * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentRangesFormattingEdits?(document: TextDocument, ranges: Range[], options: FormattingOptions, token: CancellationToken): ProviderResult; @@ -4190,7 +4504,7 @@ declare module 'vscode' { * @param ch The character that has been typed. * @param options Options controlling formatting. * @param token A cancellation token. - * @return A set of text edits or a thenable that resolves to such. The lack of a result can be + * @returns A set of text edits or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined`, `null`, or an empty array. */ provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): ProviderResult; @@ -4358,7 +4672,7 @@ declare module 'vscode' { * @param token A cancellation token. * @param context Information about how signature help was triggered. * - * @return Signature help or a thenable that resolves to such. The lack of a result can be + * @returns Signature help or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideSignatureHelp(document: TextDocument, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; @@ -4411,32 +4725,113 @@ declare module 'vscode' { * Completion item kinds. */ export enum CompletionItemKind { + /** + * The `Text` completion item kind. + */ Text = 0, + /** + * The `Method` completion item kind. + */ Method = 1, + /** + * The `Function` completion item kind. + */ Function = 2, + /** + * The `Constructor` completion item kind. + */ Constructor = 3, + /** + * The `Field` completion item kind. + */ Field = 4, + /** + * The `Variable` completion item kind. + */ Variable = 5, + /** + * The `Class` completion item kind. + */ Class = 6, + /** + * The `Interface` completion item kind. + */ Interface = 7, + /** + * The `Module` completion item kind. + */ Module = 8, + /** + * The `Property` completion item kind. + */ Property = 9, + /** + * The `Unit` completion item kind. + */ Unit = 10, + /** + * The `Value` completion item kind. + */ Value = 11, + /** + * The `Enum` completion item kind. + */ Enum = 12, + /** + * The `Keyword` completion item kind. + */ Keyword = 13, + /** + * The `Snippet` completion item kind. + */ Snippet = 14, + /** + * The `Color` completion item kind. + */ Color = 15, + /** + * The `Reference` completion item kind. + */ Reference = 17, + /** + * The `File` completion item kind. + */ File = 16, + /** + * The `Folder` completion item kind. + */ Folder = 18, + /** + * The `EnumMember` completion item kind. + */ EnumMember = 19, + /** + * The `Constant` completion item kind. + */ Constant = 20, + /** + * The `Struct` completion item kind. + */ Struct = 21, + /** + * The `Event` completion item kind. + */ Event = 22, + /** + * The `Operator` completion item kind. + */ Operator = 23, + /** + * The `TypeParameter` completion item kind. + */ TypeParameter = 24, + /** + * The `User` completion item kind. + */ User = 25, + /** + * The `Issue` completion item kind. + */ Issue = 26, } @@ -4546,7 +4941,16 @@ declare module 'vscode' { * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. * *Note 2:* A insert range must be a prefix of a replace range, that means it must be contained and starting at the same position. */ - range?: Range | { inserting: Range; replacing: Range }; + range?: Range | { + /** + * The range that should be used when insert-accepting a completion. Must be a prefix of `replaceRange`. + */ + inserting: Range; + /** + * The range that should be used when replace-accepting a completion. + */ + replacing: Range; + }; /** * An optional set of characters that when pressed while this completion is active will accept it first and @@ -4687,7 +5091,7 @@ declare module 'vscode' { * @param token A cancellation token. * @param context How the completion was triggered. * - * @return An array of completions, a {@link CompletionList completion list}, or a thenable that resolves to either. + * @returns An array of completions, a {@link CompletionList completion list}, or a thenable that resolves to either. * The lack of a result can be signaled by returning `undefined`, `null`, or an empty array. */ provideCompletionItems(document: TextDocument, position: Position, token: CancellationToken, context: CompletionContext): ProviderResult>; @@ -4708,7 +5112,7 @@ declare module 'vscode' { * * @param item A completion item currently active in the UI. * @param token A cancellation token. - * @return The resolved completion item or a thenable that resolves to of such. It is OK to return the given + * @returns The resolved completion item or a thenable that resolves to of such. It is OK to return the given * `item`. When no result is returned, the given `item` will be used. */ resolveCompletionItem?(item: T, token: CancellationToken): ProviderResult; @@ -4734,7 +5138,7 @@ declare module 'vscode' { * @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. + * @returns 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; } @@ -4898,7 +5302,7 @@ declare module 'vscode' { * * @param document The document in which the command was invoked. * @param token A cancellation token. - * @return An array of {@link DocumentLink document links} or a thenable that resolves to such. The lack of a result + * @returns An array of {@link DocumentLink document links} or a thenable that resolves to such. The lack of a result * can be signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentLinks(document: TextDocument, token: CancellationToken): ProviderResult; @@ -5024,7 +5428,7 @@ declare module 'vscode' { * * @param document The document in which the command was invoked. * @param token A cancellation token. - * @return An array of {@link ColorInformation color information} or a thenable that resolves to such. The lack of a result + * @returns An array of {@link ColorInformation color information} or a thenable that resolves to such. The lack of a result * can be signaled by returning `undefined`, `null`, or an empty array. */ provideDocumentColors(document: TextDocument, token: CancellationToken): ProviderResult; @@ -5035,10 +5439,19 @@ declare module 'vscode' { * @param color The color to show and insert. * @param context A context object with additional information * @param token A cancellation token. - * @return An array of color presentations or a thenable that resolves to such. The lack of a result + * @returns An array of color presentations or a thenable that resolves to such. The lack of a result * can be signaled by returning `undefined`, `null`, or an empty array. */ - provideColorPresentations(color: Color, context: { readonly document: TextDocument; readonly range: Range }, token: CancellationToken): ProviderResult; + provideColorPresentations(color: Color, context: { + /** + * The text document that contains the color + */ + readonly document: TextDocument; + /** + * The range in the document where the color is located. + */ + readonly range: Range; + }, token: CancellationToken): ProviderResult; } /** @@ -5194,7 +5607,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param range The range for which inlay hints should be computed. * @param token A cancellation token. - * @return An array of inlay hints or a thenable that resolves to such. + * @returns An array of inlay hints or a thenable that resolves to such. */ provideInlayHints(document: TextDocument, range: Range, token: CancellationToken): ProviderResult; @@ -5206,7 +5619,7 @@ declare module 'vscode' { * * @param hint An inlay hint. * @param token A cancellation token. - * @return The resolved inlay hint or a thenable that resolves to such. It is OK to return the given `item`. When no result is returned, the given `item` will be used. + * @returns The resolved inlay hint or a thenable that resolves to such. It is OK to return the given `item`. When no result is returned, the given `item` will be used. */ resolveInlayHint?(hint: T, token: CancellationToken): ProviderResult; } @@ -5321,6 +5734,9 @@ declare module 'vscode' { constructor(range: Range, parent?: SelectionRange); } + /** + * The selection range provider interface defines the contract between extensions and the "Expand and Shrink Selection" feature. + */ export interface SelectionRangeProvider { /** * Provide selection ranges for the given positions. @@ -5332,7 +5748,7 @@ declare module 'vscode' { * @param document The document in which the command was invoked. * @param positions The positions at which the command was invoked. * @param token A cancellation token. - * @return Selection ranges or a thenable that resolves to such. The lack of a result can be + * @returns Selection ranges or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideSelectionRanges(document: TextDocument, positions: readonly Position[], token: CancellationToken): ProviderResult; @@ -5618,7 +6034,7 @@ declare module 'vscode' { * @param document The document in which the provider was invoked. * @param position The position at which the provider was invoked. * @param token A cancellation token. - * @return A list of ranges that can be edited together + * @returns A list of ranges that can be edited together */ provideLinkedEditingRanges(document: TextDocument, position: Position, token: CancellationToken): ProviderResult; } @@ -5659,7 +6075,7 @@ declare module 'vscode' { * @param dataTransfer A {@link DataTransfer} object that holds data about what is being dragged and dropped. * @param token A cancellation token. * - * @return A {@link DocumentDropEdit} or a thenable that resolves to such. The lack of a result can be + * @returns A {@link DocumentDropEdit} or a thenable that resolves to such. The lack of a result can be * signaled by returning `undefined` or `null`. */ provideDocumentDropEdits(document: TextDocument, position: Position, dataTransfer: DataTransfer, token: CancellationToken): ProviderResult; @@ -5823,9 +6239,21 @@ declare module 'vscode' { * @deprecated */ docComment?: { + /** + * @deprecated + */ scope: string; + /** + * @deprecated + */ open: string; + /** + * @deprecated + */ lineStart: string; + /** + * @deprecated + */ close?: string; }; }; @@ -5836,9 +6264,21 @@ declare module 'vscode' { * @deprecated * Use the autoClosingPairs property in the language configuration file instead. */ __characterPairSupport?: { + /** + * @deprecated + */ autoClosingPairs: { + /** + * @deprecated + */ open: string; + /** + * @deprecated + */ close: string; + /** + * @deprecated + */ notIn?: string[]; }[]; }; @@ -5933,7 +6373,7 @@ declare module 'vscode' { * Return a value from this configuration. * * @param section Configuration name, supports _dotted_ names. - * @return The value `section` denotes or `undefined`. + * @returns The value `section` denotes or `undefined`. */ get(section: string): T | undefined; @@ -5942,7 +6382,7 @@ declare module 'vscode' { * * @param section Configuration name, supports _dotted_ names. * @param defaultValue A value should be returned when no value could be found, is `undefined`. - * @return The value `section` denotes or the default. + * @returns The value `section` denotes or the default. */ get(section: string, defaultValue: T): T; @@ -5950,7 +6390,7 @@ declare module 'vscode' { * Check if this configuration has a certain value. * * @param section Configuration name, supports _dotted_ names. - * @return `true` if the section doesn't resolve to `undefined`. + * @returns `true` if the section doesn't resolve to `undefined`. */ has(section: string): boolean; @@ -5966,21 +6406,58 @@ declare module 'vscode' { * (`editor.fontSize` vs `editor`) otherwise no result is returned. * * @param section Configuration name, supports _dotted_ names. - * @return Information about a configuration setting or `undefined`. + * @returns Information about a configuration setting or `undefined`. */ inspect(section: string): { + + /** + * The fully qualified key of the configuration value + */ key: string; + /** + * The default value which is used when no other value is defined + */ defaultValue?: T; + + /** + * The global or installation-wide value. + */ globalValue?: T; + + /** + * The workspace-specific value. + */ workspaceValue?: T; + + /** + * The workpace-folder-specific value. + */ workspaceFolderValue?: T; + /** + * Language specific default value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ defaultLanguageValue?: T; + + /** + * Language specific global value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ globalLanguageValue?: T; + + /** + * Language specific workspace value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ workspaceLanguageValue?: T; + + /** + * Language specific workspace-folder value when this configuration value is created for a {@link ConfigurationScope language scope}. + */ workspaceFolderLanguageValue?: T; + /** + * All language identifiers for which this configuration is defined. + */ languageIds?: string[]; } | undefined; @@ -6317,9 +6794,21 @@ declare module 'vscode' { /** * Represents the severity of a language status item. */ + /** + * Represents the severity level of a language status. + */ export enum LanguageStatusSeverity { + /** + * Informational severity level. + */ Information = 0, + /** + * Warning severity level. + */ Warning = 1, + /** + * Error severity level. + */ Error = 2 } @@ -6884,7 +7373,7 @@ declare module 'vscode' { * that could have problems when asynchronous usage may overlap. * @param context Information about what links are being provided for. * @param token A cancellation token. - * @return A list of terminal links for the given line. + * @returns A list of terminal links for the given line. */ provideTerminalLinks(context: TerminalLinkContext, token: CancellationToken): ProviderResult; @@ -7094,7 +7583,7 @@ declare module 'vscode' { /** * Activates this extension and returns its public API. * - * @return A promise that will resolve when this extension has been activated. + * @returns A promise that will resolve when this extension has been activated. */ activate(): Thenable; } @@ -7138,7 +7627,12 @@ declare module 'vscode' { * * *Note* that asynchronous dispose-functions aren't awaited. */ - readonly subscriptions: { dispose(): any }[]; + readonly subscriptions: { + /** + * Function to clean up resources. + */ + dispose(): any; + }[]; /** * A memento object that stores state in the context @@ -7197,7 +7691,7 @@ declare module 'vscode' { * {@linkcode ExtensionContext.extensionUri extensionUri}, e.g. `vscode.Uri.joinPath(context.extensionUri, relativePath);` * * @param relativePath A relative path to a resource contained in the extension. - * @return The absolute path of the resource. + * @returns The absolute path of the resource. */ asAbsolutePath(relativePath: string): string; @@ -7291,7 +7785,7 @@ declare module 'vscode' { /** * Returns the stored keys. * - * @return The stored keys. + * @returns The stored keys. */ keys(): readonly string[]; @@ -7299,7 +7793,7 @@ declare module 'vscode' { * Return a value. * * @param key A string. - * @return The stored value or `undefined`. + * @returns The stored value or `undefined`. */ get(key: string): T | undefined; @@ -7309,7 +7803,7 @@ declare module 'vscode' { * @param key A string. * @param defaultValue A value that should be returned when there is no * value (`undefined`) with the given key. - * @return The stored value or the defaultValue. + * @returns The stored value or the defaultValue. */ get(key: string, defaultValue: T): T; @@ -7371,9 +7865,21 @@ declare module 'vscode' { * Represents a color theme kind. */ export enum ColorThemeKind { + /** + * A light color theme. + */ Light = 1, + /** + * A dark color theme. + */ Dark = 2, + /** + * A dark high contrast color theme. + */ HighContrast = 3, + /** + * A light high contrast color theme. + */ HighContrastLight = 4 } @@ -7512,6 +8018,12 @@ declare module 'vscode' { */ readonly id: string; + /** + * Private constructor + * + * @param id Identifier of a task group. + * @param label The human-readable name of a task group. + */ private constructor(id: string, label: string); } @@ -7715,6 +8227,9 @@ declare module 'vscode' { quoting: ShellQuoting; } + /** + * Represents a task execution that happens inside a shell. + */ export class ShellExecution { /** * Creates a shell execution with a full command line. @@ -7904,7 +8419,7 @@ declare module 'vscode' { /** * Provides tasks. * @param token A cancellation token. - * @return an array of tasks + * @returns an array of tasks */ provideTasks(token: CancellationToken): ProviderResult; @@ -7923,7 +8438,7 @@ declare module 'vscode' { * * @param task The task to resolve. * @param token A cancellation token. - * @return The resolved task + * @returns The resolved task */ resolveTask(task: T, token: CancellationToken): ProviderResult; } @@ -8004,6 +8519,9 @@ declare module 'vscode' { readonly exitCode: number | undefined; } + /** + * A task filter denotes tasks by their version and types + */ export interface TaskFilter { /** * The task version as used in the tasks.json file. @@ -8027,7 +8545,7 @@ declare module 'vscode' { * * @param type The task kind type this provider is registered for. * @param provider A task provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; @@ -8037,6 +8555,7 @@ declare module 'vscode' { * contributed through extensions. * * @param filter Optional filter to select tasks of a certain type or version. + * @returns A thenable that resolves to an array of tasks. */ export function fetchTasks(filter?: TaskFilter): Thenable; @@ -8049,6 +8568,7 @@ declare module 'vscode' { * In such an environment, only CustomExecution tasks can be run. * * @param task the task to execute + * @returns A thenable that resolves to a task execution. */ export function executeTask(task: Task): Thenable; @@ -8106,6 +8626,9 @@ declare module 'vscode' { SymbolicLink = 64 } + /** + * Permissions of a file. + */ export enum FilePermission { /** * The file is readonly. @@ -8303,7 +8826,16 @@ declare module 'vscode' { * @param options Configures the watch. * @returns A disposable that tells the provider to stop watching the `uri`. */ - watch(uri: Uri, options: { readonly recursive: boolean; readonly excludes: readonly string[] }): Disposable; + watch(uri: Uri, options: { + /** + * When enabled also watch subfolders. + */ + readonly recursive: boolean; + /** + * A list of paths and pattern to exclude from watching. + */ + readonly excludes: readonly string[]; + }): Disposable; /** * Retrieve metadata about a file. @@ -8313,7 +8845,7 @@ declare module 'vscode' { * `FileType.SymbolicLink | FileType.Directory`. * * @param uri The uri of the file to retrieve metadata about. - * @return The file metadata about the file. + * @returns The file metadata about the file. * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. */ stat(uri: Uri): FileStat | Thenable; @@ -8322,7 +8854,7 @@ declare module 'vscode' { * Retrieve all entries of a {@link FileType.Directory directory}. * * @param uri The uri of the folder. - * @return An array of name/type-tuples or a thenable that resolves to such. + * @returns An array of name/type-tuples or a thenable that resolves to such. * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. */ readDirectory(uri: Uri): [string, FileType][] | Thenable<[string, FileType][]>; @@ -8341,7 +8873,7 @@ declare module 'vscode' { * Read the entire contents of a file. * * @param uri The uri of the file. - * @return An array of bytes or a thenable that resolves to such. + * @returns An array of bytes or a thenable that resolves to such. * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. */ readFile(uri: Uri): Uint8Array | Thenable; @@ -8357,7 +8889,16 @@ declare module 'vscode' { * @throws {@linkcode FileSystemError.FileExists FileExists} when `uri` already exists, `create` is set but `overwrite` is not set. * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. */ - writeFile(uri: Uri, content: Uint8Array, options: { readonly create: boolean; readonly overwrite: boolean }): void | Thenable; + writeFile(uri: Uri, content: Uint8Array, options: { + /** + * Create the file if it does not exist already. + */ + readonly create: boolean; + /** + * Overwrite the file if it does exist. + */ + readonly overwrite: boolean; + }): void | Thenable; /** * Delete a file. @@ -8367,7 +8908,12 @@ declare module 'vscode' { * @throws {@linkcode FileSystemError.FileNotFound FileNotFound} when `uri` doesn't exist. * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. */ - delete(uri: Uri, options: { readonly recursive: boolean }): void | Thenable; + delete(uri: Uri, options: { + /** + * Delete the content recursively if a folder is denoted. + */ + readonly recursive: boolean; + }): void | Thenable; /** * Rename a file or folder. @@ -8380,7 +8926,12 @@ declare module 'vscode' { * @throws {@linkcode FileSystemError.FileExists FileExists} when `newUri` exists and when the `overwrite` option is not `true`. * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. */ - rename(oldUri: Uri, newUri: Uri, options: { readonly overwrite: boolean }): void | Thenable; + rename(oldUri: Uri, newUri: Uri, options: { + /** + * Overwrite the file if it does exist. + */ + readonly overwrite: boolean; + }): void | Thenable; /** * Copy files or folders. Implementing this function is optional but it will speedup @@ -8394,7 +8945,12 @@ declare module 'vscode' { * @throws {@linkcode FileSystemError.FileExists FileExists} when `destination` exists and when the `overwrite` option is not `true`. * @throws {@linkcode FileSystemError.NoPermissions NoPermissions} when permissions aren't sufficient. */ - copy?(source: Uri, destination: Uri, options: { readonly overwrite: boolean }): void | Thenable; + copy?(source: Uri, destination: Uri, options: { + /** + * Overwrite the file if it does exist. + */ + readonly overwrite: boolean; + }): void | Thenable; } /** @@ -8411,7 +8967,7 @@ declare module 'vscode' { * Retrieve metadata about a file. * * @param uri The uri of the file to retrieve metadata about. - * @return The file metadata about the file. + * @returns The file metadata about the file. */ stat(uri: Uri): Thenable; @@ -8419,7 +8975,7 @@ declare module 'vscode' { * Retrieve all entries of a {@link FileType.Directory directory}. * * @param uri The uri of the folder. - * @return An array of name/type-tuples or a thenable that resolves to such. + * @returns An array of name/type-tuples or a thenable that resolves to such. */ readDirectory(uri: Uri): Thenable<[string, FileType][]>; @@ -8437,7 +8993,7 @@ declare module 'vscode' { * Read the entire contents of a file. * * @param uri The uri of the file. - * @return An array of bytes or a thenable that resolves to such. + * @returns An array of bytes or a thenable that resolves to such. */ readFile(uri: Uri): Thenable; @@ -8455,7 +9011,16 @@ declare module 'vscode' { * @param uri The resource that is to be deleted. * @param options Defines if trash can should be used and if deletion of folders is recursive */ - delete(uri: Uri, options?: { recursive?: boolean; useTrash?: boolean }): Thenable; + delete(uri: Uri, options?: { + /** + * Delete the content recursively if a folder is denoted. + */ + recursive?: boolean; + /** + * Use the os's trashcan instead of permanently deleting files whenever possible. + */ + useTrash?: boolean; + }): Thenable; /** * Rename a file or folder. @@ -8464,7 +9029,12 @@ declare module 'vscode' { * @param target The new location. * @param options Defines if existing files should be overwritten. */ - rename(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; + rename(source: Uri, target: Uri, options?: { + /** + * Overwrite the file if it does exist. + */ + overwrite?: boolean; + }): Thenable; /** * Copy files or folders. @@ -8473,7 +9043,12 @@ declare module 'vscode' { * @param target The destination location. * @param options Defines if existing files should be overwritten. */ - copy(source: Uri, target: Uri, options?: { overwrite?: boolean }): Thenable; + copy(source: Uri, target: Uri, options?: { + /** + * Overwrite the file if it does exist. + */ + overwrite?: boolean; + }): Thenable; /** * Check if a given file system supports writing files. @@ -8484,7 +9059,7 @@ declare module 'vscode' { * * @param scheme The scheme of the filesystem, for example `file` or `git`. * - * @return `true` if the file system supports writing, `false` if it does not + * @returns `true` if the file system supports writing, `false` if it does not * support writing (i.e. it is readonly), and `undefined` if the editor does not * know about the filesystem. */ @@ -8622,7 +9197,7 @@ declare module 'vscode' { * efficiently transferred to the webview and will also be correctly recreated inside * of the webview. * - * @return A promise that resolves when the message is posted to a webview or when it is + * @returns A promise that resolves when the message is posted to a webview or when it is * dropped because the message was not deliverable. * * Returns `true` if the message was posted to the webview. Messages can only be posted to @@ -8709,7 +9284,16 @@ declare module 'vscode' { /** * Icon for the panel shown in UI. */ - iconPath?: Uri | { readonly light: Uri; readonly dark: Uri }; + iconPath?: Uri | { + /** + * The icon path for the light theme. + */ + readonly light: Uri; + /** + * The icon path for the dark theme. + */ + readonly dark: Uri; + }; /** * {@linkcode Webview} belonging to the panel. @@ -8826,7 +9410,7 @@ declare module 'vscode' { * serializer must restore the webview's `.html` and hook up all webview events. * @param state Persisted state from the webview content. * - * @return Thenable indicating that the webview has been fully restored. + * @returns Thenable indicating that the webview has been fully restored. */ deserializeWebviewPanel(webviewPanel: WebviewPanel, state: T): Thenable; } @@ -8955,7 +9539,7 @@ declare module 'vscode' { * @param context Additional metadata about the view being resolved. * @param token Cancellation token indicating that the view being provided is no longer needed. * - * @return Optional thenable indicating that the view has been fully resolved. + * @returns Optional thenable indicating that the view has been fully resolved. */ resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Thenable | void; } @@ -8986,7 +9570,7 @@ declare module 'vscode' { * * @param token A cancellation token that indicates the result is no longer needed. * - * @return Thenable indicating that the custom editor has been resolved. + * @returns Thenable indicating that the custom editor has been resolved. */ resolveCustomTextEditor(document: TextDocument, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; } @@ -9145,7 +9729,7 @@ declare module 'vscode' { * @param openContext Additional information about the opening custom document. * @param token A cancellation token that indicates the result is no longer needed. * - * @return The custom document. + * @returns The custom document. */ openCustomDocument(uri: Uri, openContext: CustomDocumentOpenContext, token: CancellationToken): Thenable | T; @@ -9164,7 +9748,7 @@ declare module 'vscode' { * * @param token A cancellation token that indicates the result is no longer needed. * - * @return Optional thenable indicating that the custom editor has been resolved. + * @returns Optional thenable indicating that the custom editor has been resolved. */ resolveCustomEditor(document: T, webviewPanel: WebviewPanel, token: CancellationToken): Thenable | void; } @@ -9216,7 +9800,7 @@ declare module 'vscode' { * @param document Document to save. * @param cancellation Token that signals the save is no longer required (for example, if another save was triggered). * - * @return Thenable signaling that saving has completed. + * @returns Thenable signaling that saving has completed. */ saveCustomDocument(document: T, cancellation: CancellationToken): Thenable; @@ -9232,7 +9816,7 @@ declare module 'vscode' { * @param destination Location to save to. * @param cancellation Token that signals the save is no longer required. * - * @return Thenable signaling that saving has completed. + * @returns Thenable signaling that saving has completed. */ saveCustomDocumentAs(document: T, destination: Uri, cancellation: CancellationToken): Thenable; @@ -9249,7 +9833,7 @@ declare module 'vscode' { * @param document Document to revert. * @param cancellation Token that signals the revert is no longer required. * - * @return Thenable signaling that the change has completed. + * @returns Thenable signaling that the change has completed. */ revertCustomDocument(document: T, cancellation: CancellationToken): Thenable; @@ -9517,7 +10101,7 @@ declare module 'vscode' { * Any other scheme will be handled as if the provided URI is a workspace URI. In that case, the method will return * a URI which, when handled, will make the editor open the workspace. * - * @return A uri that can be used on the client machine. + * @returns A uri that can be used on the client machine. */ export function asExternalUri(target: Uri): Thenable; @@ -9580,7 +10164,7 @@ declare module 'vscode' { * @param command A unique identifier for the command. * @param callback A command handler function. * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. + * @returns Disposable which unregisters this command on disposal. */ export function registerCommand(command: string, callback: (...args: any[]) => any, thisArg?: any): Disposable; @@ -9597,7 +10181,7 @@ declare module 'vscode' { * @param command A unique identifier for the command. * @param callback A command handler function with access to an {@link TextEditor editor} and an {@link TextEditorEdit edit}. * @param thisArg The `this` context used when invoking the handler function. - * @return Disposable which unregisters this command on disposal. + * @returns Disposable which unregisters this command on disposal. */ export function registerTextEditorCommand(command: string, callback: (textEditor: TextEditor, edit: TextEditorEdit, ...args: any[]) => void, thisArg?: any): Disposable; @@ -9612,7 +10196,7 @@ declare module 'vscode' { * * @param command Identifier of the command to execute. * @param rest Parameters passed to the command function. - * @return A thenable that resolves to the returned value of the given command. Returns `undefined` when + * @returns A thenable that resolves to the returned value of the given command. Returns `undefined` when * the command handler function doesn't return anything. */ export function executeCommand(command: string, ...rest: any[]): Thenable; @@ -9622,7 +10206,7 @@ declare module 'vscode' { * treated as internal commands. * * @param filterInternal Set `true` to not see internal commands (starting with an underscore) - * @return Thenable that resolves to a list of command ids. + * @returns Thenable that resolves to a list of command ids. */ export function getCommands(filterInternal?: boolean): Thenable; } @@ -9801,7 +10385,7 @@ declare module 'vscode' { * Columns that do not exist will be created as needed up to the maximum of {@linkcode ViewColumn.Nine}. Use {@linkcode ViewColumn.Beside} * to open the editor to the side of the currently active one. * @param preserveFocus When `true` the editor will not take focus. - * @return A promise that resolves to an {@link TextEditor editor}. + * @returns A promise that resolves to an {@link TextEditor editor}. */ export function showTextDocument(document: TextDocument, column?: ViewColumn, preserveFocus?: boolean): Thenable; @@ -9811,7 +10395,7 @@ declare module 'vscode' { * * @param document A text document to be shown. * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. - * @return A promise that resolves to an {@link TextEditor editor}. + * @returns A promise that resolves to an {@link TextEditor editor}. */ export function showTextDocument(document: TextDocument, options?: TextDocumentShowOptions): Thenable; @@ -9822,7 +10406,7 @@ declare module 'vscode' { * * @param uri A resource identifier. * @param options {@link TextDocumentShowOptions Editor options} to configure the behavior of showing the {@link TextEditor editor}. - * @return A promise that resolves to an {@link TextEditor editor}. + * @returns A promise that resolves to an {@link TextEditor editor}. */ export function showTextDocument(uri: Uri, options?: TextDocumentShowOptions): Thenable; @@ -9832,7 +10416,7 @@ declare module 'vscode' { * @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}. + * @returns A promise that resolves to an {@link NotebookEditor notebook editor}. */ export function showNotebookDocument(document: NotebookDocument, options?: NotebookDocumentShowOptions): Thenable; @@ -9840,7 +10424,7 @@ declare module 'vscode' { * Create a TextEditorDecorationType that can be used to add decorations to text editors. * * @param options Rendering options for the decoration type. - * @return A new decoration type instance. + * @returns A new decoration type instance. */ export function createTextEditorDecorationType(options: DecorationRenderOptions): TextEditorDecorationType; @@ -9850,7 +10434,7 @@ declare module 'vscode' { * * @param message The message to show. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showInformationMessage(message: string, ...items: T[]): Thenable; @@ -9861,7 +10445,7 @@ declare module 'vscode' { * @param message The message to show. * @param options Configures the behaviour of the message. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; @@ -9872,7 +10456,7 @@ declare module 'vscode' { * * @param message The message to show. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showInformationMessage(message: string, ...items: T[]): Thenable; @@ -9884,7 +10468,7 @@ declare module 'vscode' { * @param message The message to show. * @param options Configures the behaviour of the message. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showInformationMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; @@ -9895,7 +10479,7 @@ declare module 'vscode' { * * @param message The message to show. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showWarningMessage(message: string, ...items: T[]): Thenable; @@ -9907,7 +10491,7 @@ declare module 'vscode' { * @param message The message to show. * @param options Configures the behaviour of the message. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; @@ -9918,7 +10502,7 @@ declare module 'vscode' { * * @param message The message to show. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showWarningMessage(message: string, ...items: T[]): Thenable; @@ -9930,7 +10514,7 @@ declare module 'vscode' { * @param message The message to show. * @param options Configures the behaviour of the message. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showWarningMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; @@ -9941,7 +10525,7 @@ declare module 'vscode' { * * @param message The message to show. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showErrorMessage(message: string, ...items: T[]): Thenable; @@ -9953,7 +10537,7 @@ declare module 'vscode' { * @param message The message to show. * @param options Configures the behaviour of the message. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; @@ -9964,7 +10548,7 @@ declare module 'vscode' { * * @param message The message to show. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showErrorMessage(message: string, ...items: T[]): Thenable; @@ -9976,7 +10560,7 @@ declare module 'vscode' { * @param message The message to show. * @param options Configures the behaviour of the message. * @param items A set of items that will be rendered as actions in the message. - * @return A thenable that resolves to the selected item or `undefined` when being dismissed. + * @returns A thenable that resolves to the selected item or `undefined` when being dismissed. */ export function showErrorMessage(message: string, options: MessageOptions, ...items: T[]): Thenable; @@ -9986,9 +10570,9 @@ declare module 'vscode' { * @param items An array of strings, or a promise that resolves to an array of strings. * @param options Configures the behavior of the selection list. * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selected items or `undefined`. + * @returns A promise that resolves to the selected items or `undefined`. */ - export function showQuickPick(items: readonly string[] | Thenable, options: QuickPickOptions & { canPickMany: true }, token?: CancellationToken): Thenable; + export function showQuickPick(items: readonly string[] | Thenable, options: QuickPickOptions & { /** literal-type defines return type */canPickMany: true }, token?: CancellationToken): Thenable; /** * Shows a selection list. @@ -9996,7 +10580,7 @@ declare module 'vscode' { * @param items An array of strings, or a promise that resolves to an array of strings. * @param options Configures the behavior of the selection list. * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selection or `undefined`. + * @returns A promise that resolves to the selection or `undefined`. */ export function showQuickPick(items: readonly string[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; @@ -10006,9 +10590,9 @@ declare module 'vscode' { * @param items An array of items, or a promise that resolves to an array of items. * @param options Configures the behavior of the selection list. * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selected items or `undefined`. + * @returns A promise that resolves to the selected items or `undefined`. */ - export function showQuickPick(items: readonly T[] | Thenable, options: QuickPickOptions & { canPickMany: true }, token?: CancellationToken): Thenable; + export function showQuickPick(items: readonly T[] | Thenable, options: QuickPickOptions & { /** literal-type defines return type */ canPickMany: true }, token?: CancellationToken): Thenable; /** * Shows a selection list. @@ -10016,7 +10600,7 @@ declare module 'vscode' { * @param items An array of items, or a promise that resolves to an array of items. * @param options Configures the behavior of the selection list. * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to the selected item or `undefined`. + * @returns A promise that resolves to the selected item or `undefined`. */ export function showQuickPick(items: readonly T[] | Thenable, options?: QuickPickOptions, token?: CancellationToken): Thenable; @@ -10025,7 +10609,7 @@ declare module 'vscode' { * Returns `undefined` if no folder is open. * * @param options Configures the behavior of the workspace folder list. - * @return A promise that resolves to the workspace folder or `undefined`. + * @returns A promise that resolves to the workspace folder or `undefined`. */ export function showWorkspaceFolderPick(options?: WorkspaceFolderPickOptions): Thenable; @@ -10056,7 +10640,7 @@ declare module 'vscode' { * * @param options Configures the behavior of the input box. * @param token A token that can be used to signal cancellation. - * @return A promise that resolves to a string the user provided or to `undefined` in case of dismissal. + * @returns A promise that resolves to a string the user provided or to `undefined` in case of dismissal. */ export function showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable; @@ -10068,7 +10652,7 @@ declare module 'vscode' { * is easier to use. {@link window.createQuickPick} should be used * when {@link window.showQuickPick} does not offer the required flexibility. * - * @return A new {@link QuickPick}. + * @returns A new {@link QuickPick}. */ export function createQuickPick(): QuickPick; @@ -10079,7 +10663,7 @@ declare module 'vscode' { * is easier to use. {@link window.createInputBox} should be used * when {@link window.showInputBox} does not offer the required flexibility. * - * @return A new {@link InputBox}. + * @returns A new {@link InputBox}. */ export function createInputBox(): InputBox; @@ -10092,6 +10676,7 @@ declare module 'vscode' { * * @param name Human-readable string which will be used to represent the channel in the UI. * @param languageId The identifier of the language associated with the channel. + * @returns A new output channel. */ export function createOutputChannel(name: string, languageId?: string): OutputChannel; @@ -10100,8 +10685,9 @@ declare module 'vscode' { * * @param name Human-readable string which will be used to represent the channel in the UI. * @param options Options for the log output channel. + * @returns A new log output channel. */ - export function createOutputChannel(name: string, options: { log: true }): LogOutputChannel; + export function createOutputChannel(name: string, options: { /** literal-type defines return type */log: true }): LogOutputChannel; /** * Create and show a new webview panel. @@ -10111,9 +10697,18 @@ declare module 'vscode' { * @param showOptions Where to show the webview in the editor. If preserveFocus is set, the new webview will not take focus. * @param options Settings for the new panel. * - * @return New webview panel. + * @returns New webview panel. */ - export function createWebviewPanel(viewType: string, title: string, showOptions: ViewColumn | { readonly viewColumn: ViewColumn; readonly preserveFocus?: boolean }, options?: WebviewPanelOptions & WebviewOptions): WebviewPanel; + export function createWebviewPanel(viewType: string, title: string, showOptions: ViewColumn | { + /** + * The view column in which the {@link WebviewPanel} should be shown. + */ + readonly viewColumn: ViewColumn; + /** + * An optional flag that when `true` will stop the panel from taking focus. + */ + readonly preserveFocus?: boolean; + }, options?: WebviewPanelOptions & WebviewOptions): WebviewPanel; /** * Set a message to the status bar. This is a short hand for the more powerful @@ -10121,7 +10716,7 @@ declare module 'vscode' { * * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. * @param hideAfterTimeout Timeout in milliseconds after which the message will be disposed. - * @return A disposable which hides the status bar message. + * @returns A disposable which hides the status bar message. */ export function setStatusBarMessage(text: string, hideAfterTimeout: number): Disposable; @@ -10131,7 +10726,7 @@ declare module 'vscode' { * * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. * @param hideWhenDone Thenable on which completion (resolve or reject) the message will be disposed. - * @return A disposable which hides the status bar message. + * @returns A disposable which hides the status bar message. */ export function setStatusBarMessage(text: string, hideWhenDone: Thenable): Disposable; @@ -10143,7 +10738,7 @@ declare module 'vscode' { * longer used. * * @param text The message to show, supports icon substitution as in status bar {@link StatusBarItem.text items}. - * @return A disposable which hides the status bar message. + * @returns A disposable which hides the status bar message. */ export function setStatusBarMessage(text: string): Disposable; @@ -10155,7 +10750,7 @@ declare module 'vscode' { * * @param task A callback returning a promise. Progress increments can be reported with * the provided {@link Progress}-object. - * @return The thenable the task did return. + * @returns The thenable the task did return. */ export function withScmProgress(task: (progress: Progress) => Thenable): Thenable; @@ -10164,6 +10759,7 @@ declare module 'vscode' { * and while the promise it returned isn't resolved nor rejected. The location at which * progress should show (and other details) is defined via the passed {@linkcode ProgressOptions}. * + * @param options A {@linkcode ProgressOptions}-object describing the options to use for showing progress, like its location * @param task A callback returning a promise. Progress state can be reported with * the provided {@link Progress}-object. * @@ -10176,9 +10772,18 @@ declare module 'vscode' { * Note that currently only `ProgressLocation.Notification` is supporting to show a cancel button to cancel the * long running operation. * - * @return The thenable the task-callback returned. + * @returns The thenable the task-callback returned. */ - export function withProgress(options: ProgressOptions, task: (progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken) => Thenable): Thenable; + export function withProgress(options: ProgressOptions, task: (progress: Progress<{ + /** + * A progress message that represents a chunk of work + */ + message?: string; + /** + * An increment for discrete progress. Increments will be summed up until 100% is reached + */ + increment?: number; + }>, token: CancellationToken) => Thenable): Thenable; /** * Creates a status bar {@link StatusBarItem item}. @@ -10186,7 +10791,7 @@ declare module 'vscode' { * @param id The identifier of the item. Must be unique within the extension. * @param alignment The alignment of the item. * @param priority The priority of the item. Higher values mean the item should be shown more to the left. - * @return A new status bar item. + * @returns A new status bar item. */ export function createStatusBarItem(id: string, alignment?: StatusBarAlignment, priority?: number): StatusBarItem; @@ -10196,7 +10801,7 @@ declare module 'vscode' { * @see {@link createStatusBarItem} for creating a status bar item with an identifier. * @param alignment The alignment of the item. * @param priority The priority of the item. Higher values mean the item should be shown more to the left. - * @return A new status bar item. + * @returns A new status bar item. */ export function createStatusBarItem(alignment?: StatusBarAlignment, priority?: number): StatusBarItem; @@ -10209,7 +10814,7 @@ declare module 'vscode' { * @param shellArgs Optional args for the custom shell executable. A string can be used on Windows only which * allows specifying shell args in * [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6). - * @return A new Terminal. + * @returns A new Terminal. * @throws When running in an environment where a new process cannot be started. */ export function createTerminal(name?: string, shellPath?: string, shellArgs?: readonly string[] | string): Terminal; @@ -10218,7 +10823,7 @@ declare module 'vscode' { * Creates a {@link Terminal} with a backing shell process. * * @param options A TerminalOptions object describing the characteristics of the new terminal. - * @return A new Terminal. + * @returns A new Terminal. * @throws When running in an environment where a new process cannot be started. */ export function createTerminal(options: TerminalOptions): Terminal; @@ -10228,7 +10833,7 @@ declare module 'vscode' { * * @param options An {@link ExtensionTerminalOptions} object describing * the characteristics of the new terminal. - * @return A new Terminal. + * @returns A new Terminal. */ export function createTerminal(options: ExtensionTerminalOptions): Terminal; @@ -10240,6 +10845,7 @@ declare module 'vscode' { * * @param viewId Id of the view contributed using the extension point `views`. * @param treeDataProvider A {@link TreeDataProvider} that provides tree data for the view + * @returns A {@link Disposable disposable} that unregisters the {@link TreeDataProvider}. */ export function registerTreeDataProvider(viewId: string, treeDataProvider: TreeDataProvider): Disposable; @@ -10271,6 +10877,7 @@ declare module 'vscode' { * the current extension is about to be handled. * * @param handler The uri handler to register for this extension. + * @returns A {@link Disposable disposable} that unregisters the handler. */ export function registerUriHandler(handler: UriHandler): Disposable; @@ -10284,6 +10891,7 @@ declare module 'vscode' { * * @param viewType Type of the webview panel that can be serialized. * @param serializer Webview serializer. + * @returns A {@link Disposable disposable} that unregisters the serializer. */ export function registerWebviewPanelSerializer(viewType: string, serializer: WebviewPanelSerializer): Disposable; @@ -10294,7 +10902,7 @@ declare module 'vscode' { * `views` contribution in the package.json. * @param provider Provider for the webview views. * - * @return Disposable that unregisters the provider. + * @returns Disposable that unregisters the provider. */ export function registerWebviewViewProvider(viewId: string, provider: WebviewViewProvider, options?: { /** @@ -10333,7 +10941,7 @@ declare module 'vscode' { * @param provider Provider that resolves custom editors. * @param options Options for the provider. * - * @return Disposable that unregisters the provider. + * @returns Disposable that unregisters the provider. */ export function registerCustomEditorProvider(viewType: string, provider: CustomTextEditorProvider | CustomReadonlyEditorProvider | CustomEditorProvider, options?: { /** @@ -10361,21 +10969,23 @@ declare module 'vscode' { /** * Register provider that enables the detection and handling of links within the terminal. * @param provider The provider that provides the terminal links. - * @return Disposable that unregisters the provider. + * @returns Disposable that unregisters the provider. */ export function registerTerminalLinkProvider(provider: TerminalLinkProvider): Disposable; /** * Registers a provider for a contributed terminal profile. + * * @param id The ID of the contributed terminal profile. * @param provider The terminal profile provider. + * @returns A {@link Disposable disposable} that unregisters the provider. */ export function registerTerminalProfileProvider(id: string, provider: TerminalProfileProvider): Disposable; /** * Register a file decoration provider. * * @param provider A {@link FileDecorationProvider}. - * @return A {@link Disposable} that unregisters the provider. + * @returns A {@link Disposable} that unregisters the provider. */ export function registerFileDecorationProvider(provider: FileDecorationProvider): Disposable; @@ -10753,11 +11363,24 @@ declare module 'vscode' { * In order to not to select, set the option `select` to `false`. * In order to focus, set the option `focus` to `true`. * In order to expand the revealed element, set the option `expand` to `true`. To expand recursively set `expand` to the number of levels to expand. - * **NOTE:** You can expand only to 3 levels maximum. * - * **NOTE:** The {@link TreeDataProvider} that the `TreeView` {@link window.createTreeView is registered with} with must implement {@link TreeDataProvider.getParent getParent} method to access this API. + * * *NOTE:* You can expand only to 3 levels maximum. + * * *NOTE:* The {@link TreeDataProvider} that the `TreeView` {@link window.createTreeView is registered with} with must implement {@link TreeDataProvider.getParent getParent} method to access this API. */ - reveal(element: T, options?: { select?: boolean; focus?: boolean; expand?: boolean | number }): Thenable; + reveal(element: T, options?: { + /** + * If true, then the element will be selected. + */ + select?: boolean; + /** + * If true, then the element will be focused. + */ + focus?: boolean; + /** + * If true, then the element will be expanded. If a number is passed, then up to that number of levels of children will be expanded + */ + expand?: boolean | number; + }): Thenable; } /** @@ -10775,7 +11398,7 @@ declare module 'vscode' { * Get {@link TreeItem} representation of the `element` * * @param element The element for which {@link TreeItem} representation is asked for. - * @return TreeItem representation of the element. + * @returns TreeItem representation of the element. */ getTreeItem(element: T): TreeItem | Thenable; @@ -10783,7 +11406,7 @@ declare module 'vscode' { * Get the children of `element` or root if no element is passed. * * @param element The element from which the provider gets children. Can be `undefined`. - * @return Children of `element` or root if no element is passed. + * @returns Children of `element` or root if no element is passed. */ getChildren(element?: T): ProviderResult; @@ -10794,7 +11417,7 @@ declare module 'vscode' { * **NOTE:** This method should be implemented in order to access {@link TreeView.reveal reveal} API. * * @param element The element for which the parent has to be returned. - * @return Parent of `element`. + * @returns Parent of `element`. */ getParent?(element: T): ProviderResult; @@ -10816,12 +11439,15 @@ declare module 'vscode' { * @param item Undefined properties of `item` should be set then `item` should be returned. * @param element The object associated with the TreeItem. * @param token A cancellation token. - * @return The resolved tree item or a thenable that resolves to such. It is OK to return the given + * @returns The resolved tree item or a thenable that resolves to such. It is OK to return the given * `item`. When no result is returned, the given `item` will be used. */ resolveTreeItem?(item: TreeItem, element: T, token: CancellationToken): ProviderResult; } + /** + * A tree item is an UI element of the tree. Tree items are created by the {@link TreeDataProvider data provider}. + */ export class TreeItem { /** * A human-readable string describing this item. When `falsy`, it is derived from {@link TreeItem.resourceUri resourceUri}. @@ -10840,7 +11466,16 @@ declare module 'vscode' { * When `falsy`, {@link ThemeIcon.Folder Folder Theme Icon} is assigned, if item is collapsible otherwise {@link ThemeIcon.File File Theme Icon}. * When a file or folder {@link ThemeIcon} is specified, icon is derived from the current file icon theme for the specified theme icon using {@link TreeItem.resourceUri resourceUri} (if provided). */ - iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; + iconPath?: string | Uri | { + /** + * The icon path for the light theme. + */ + light: string | Uri; + /** + * The icon path for the dark theme. + */ + dark: string | Uri; + } | ThemeIcon; /** * A human-readable string which is rendered less prominent. @@ -10906,7 +11541,20 @@ declare module 'vscode' { * {@link TreeItemCheckboxState TreeItemCheckboxState} of the tree item. * {@link TreeDataProvider.onDidChangeTreeData onDidChangeTreeData} should be fired when {@link TreeItem.checkboxState checkboxState} changes. */ - checkboxState?: TreeItemCheckboxState | { readonly state: TreeItemCheckboxState; readonly tooltip?: string; readonly accessibilityInformation?: AccessibilityInformation }; + checkboxState?: TreeItemCheckboxState | { + /** + * The {@link TreeItemCheckboxState} of the tree item + */ + readonly state: TreeItemCheckboxState; + /** + * A tooltip for the checkbox + */ + readonly tooltip?: string; + /** + * Accessibility information used when screen readers interact with this checkbox + */ + readonly accessibilityInformation?: AccessibilityInformation; + }; /** * @param label A human-readable string describing this item @@ -11028,7 +11676,16 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; /** * The icon {@link ThemeColor} for the terminal. @@ -11067,7 +11724,16 @@ declare module 'vscode' { /** * The icon path or {@link ThemeIcon} for the terminal. */ - iconPath?: Uri | { light: Uri; dark: Uri } | ThemeIcon; + iconPath?: Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; /** * The icon {@link ThemeColor} for the terminal. @@ -11475,7 +12141,7 @@ declare module 'vscode' { * returned. For instance, if the 'workspaceFolder' parameter is not specified, the collection that applies * across all workspace folders will be returned. * - * @return Environment variable collection for the passed in scope. + * @returns Environment variable collection for the passed in scope. */ getScoped(scope: EnvironmentVariableScope): EnvironmentVariableCollection; } @@ -11524,7 +12190,12 @@ declare module 'vscode' { /** * The location at which progress should show. */ - location: ProgressLocation | { viewId: string }; + location: ProgressLocation | { + /** + * The identifier of a view for which progress should be shown. + */ + viewId: string; + }; /** * A human-readable string which will be used to describe the @@ -11701,7 +12372,7 @@ declare module 'vscode' { */ matchOnDetail: boolean; - /* + /** * An optional flag to maintain the scroll position of the quick pick when the quick pick items are updated. Defaults to false. */ keepScrollPosition?: boolean; @@ -11803,7 +12474,16 @@ declare module 'vscode' { /** * Icon for the button. */ - readonly iconPath: Uri | { light: Uri; dark: Uri } | ThemeIcon; + readonly iconPath: Uri | { + /** + * The icon path for the light theme. + */ + light: Uri; + /** + * The icon path for the dark theme. + */ + dark: Uri; + } | ThemeIcon; /** * An optional tooltip. @@ -11867,6 +12547,9 @@ declare module 'vscode' { readonly text: string; } + /** + * Reasons for why a text document has changed. + */ export enum TextDocumentChangeReason { /** The text change is caused by an undo operation. */ Undo = 1, @@ -12107,7 +12790,16 @@ declare module 'vscode' { /** * The files that are going to be renamed. */ - readonly files: ReadonlyArray<{ readonly oldUri: Uri; readonly newUri: Uri }>; + readonly files: ReadonlyArray<{ + /** + * The old uri of a file. + */ + readonly oldUri: Uri; + /** + * The new uri of a file. + */ + readonly newUri: Uri; + }>; /** * Allows to pause the event and to apply a {@link WorkspaceEdit workspace edit}. @@ -12147,7 +12839,16 @@ declare module 'vscode' { /** * The files that got renamed. */ - readonly files: ReadonlyArray<{ readonly oldUri: Uri; readonly newUri: Uri }>; + readonly files: ReadonlyArray<{ + /** + * The old uri of a file. + */ + readonly oldUri: Uri; + /** + * The new uri of a file. + */ + readonly newUri: Uri; + }>; } /** @@ -12296,7 +12997,7 @@ declare module 'vscode' { * * returns the *input* when the given uri is a workspace folder itself * * @param uri An uri. - * @return A workspace folder or `undefined` + * @returns A workspace folder or `undefined` */ export function getWorkspaceFolder(uri: Uri): WorkspaceFolder | undefined; @@ -12310,7 +13011,7 @@ declare module 'vscode' { * @param includeWorkspaceFolder When `true` and when the given path is contained inside a * workspace folder the name of the workspace is prepended. Defaults to `true` when there are * multiple workspace folders and `false` otherwise. - * @return A path relative to the root or the input. + * @returns A path relative to the root or the input. */ export function asRelativePath(pathOrUri: string | Uri, includeWorkspaceFolder?: boolean): string; @@ -12354,10 +13055,19 @@ declare module 'vscode' { * @param deleteCount the optional number of workspace folders to remove. * @param workspaceFoldersToAdd the optional variable set of workspace folders to add in place of the deleted ones. * Each workspace is identified with a mandatory URI and an optional name. - * @return true if the operation was successfully started and false otherwise if arguments were used that would result + * @returns true if the operation was successfully started and false otherwise if arguments were used that would result * in invalid workspace folder state (e.g. 2 folders with the same URI). */ - export function updateWorkspaceFolders(start: number, deleteCount: number | undefined | null, ...workspaceFoldersToAdd: { readonly uri: Uri; readonly name?: string }[]): boolean; + export function updateWorkspaceFolders(start: number, deleteCount: number | undefined | null, ...workspaceFoldersToAdd: { + /** + * The uri of a workspace folder that's to be added. + */ + readonly uri: Uri; + /** + * The name of a workspace folder that's to be added. + */ + readonly name?: string; + }[]): boolean; /** * Creates a file system watcher that is notified on file events (create, change, delete) @@ -12478,7 +13188,7 @@ declare module 'vscode' { * @param ignoreCreateEvents Ignore when files have been created. * @param ignoreChangeEvents Ignore when files have been changed. * @param ignoreDeleteEvents Ignore when files have been deleted. - * @return A new file system watcher instance. Must be disposed when no longer needed. + * @returns A new file system watcher instance. Must be disposed when no longer needed. */ export function createFileSystemWatcher(globPattern: GlobPattern, ignoreCreateEvents?: boolean, ignoreChangeEvents?: boolean, ignoreDeleteEvents?: boolean): FileSystemWatcher; @@ -12496,7 +13206,7 @@ declare module 'vscode' { * but not `search.exclude`) will apply. When `null`, no excludes will apply. * @param maxResults An upper-bound for the result. * @param token A token that can be used to signal cancellation to the underlying search engine. - * @return A thenable that resolves to an array of resource identifiers. Will return no results if no + * @returns A thenable that resolves to an array of resource identifiers. Will return no results if no * {@link workspace.workspaceFolders workspace folders} are opened. */ export function findFiles(include: GlobPattern, exclude?: GlobPattern | null, maxResults?: number, token?: CancellationToken): Thenable; @@ -12505,7 +13215,7 @@ declare module 'vscode' { * Save all dirty files. * * @param includeUntitled Also save files that have been created during this session. - * @return A thenable that resolves when the files have been saved. Will return `false` + * @returns A thenable that resolves when the files have been saved. Will return `false` * for any file that failed to save. */ export function saveAll(includeUntitled?: boolean): Thenable; @@ -12525,7 +13235,7 @@ declare module 'vscode' { * * @param edit A workspace edit. * @param metadata Optional {@link WorkspaceEditMetadata metadata} for the edit. - * @return A thenable that resolves when the edit could be applied. + * @returns A thenable that resolves when the edit could be applied. */ export function applyEdit(edit: WorkspaceEdit, metadata?: WorkspaceEditMetadata): Thenable; @@ -12551,7 +13261,7 @@ declare module 'vscode' { * {@linkcode workspace.onDidCloseTextDocument onDidClose}-event can occur at any time after opening it. * * @param uri Identifies the resource to open. - * @return A promise that resolves to a {@link TextDocument document}. + * @returns A promise that resolves to a {@link TextDocument document}. */ export function openTextDocument(uri: Uri): Thenable; @@ -12560,7 +13270,7 @@ declare module 'vscode' { * * @see {@link workspace.openTextDocument} * @param fileName A name of a file on disk. - * @return A promise that resolves to a {@link TextDocument document}. + * @returns A promise that resolves to a {@link TextDocument document}. */ export function openTextDocument(fileName: string): Thenable; @@ -12570,9 +13280,18 @@ declare module 'vscode' { * specify the *language* and/or the *content* of the document. * * @param options Options to control how the document will be created. - * @return A promise that resolves to a {@link TextDocument document}. + * @returns A promise that resolves to a {@link TextDocument document}. */ - export function openTextDocument(options?: { language?: string; content?: string }): Thenable; + export function openTextDocument(options?: { + /** + * The {@link TextDocument.languageId language} of the document. + */ + language?: string; + /** + * The initial contents of the document. + */ + content?: string; + }): Thenable; /** * Register a text document content provider. @@ -12581,7 +13300,7 @@ declare module 'vscode' { * * @param scheme The uri-scheme to register for. * @param provider A content provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerTextDocumentContentProvider(scheme: string, provider: TextDocumentContentProvider): Disposable; @@ -12703,7 +13422,7 @@ declare module 'vscode' { * @param notebookType A notebook. * @param serializer A notebook serializer. * @param options Optional context options that define what parts of a notebook should be persisted - * @return A {@link Disposable} that unregisters this serializer when being disposed. + * @returns A {@link Disposable} that unregisters this serializer when being disposed. */ export function registerNotebookSerializer(notebookType: string, serializer: NotebookSerializer, options?: NotebookDocumentContentOptions): Disposable; @@ -12803,7 +13522,7 @@ declare module 'vscode' { * * @param section A dot-separated identifier. * @param scope A scope for which the configuration is asked for. - * @return The full configuration or a subset. + * @returns The full configuration or a subset. */ export function getConfiguration(section?: string, scope?: ConfigurationScope | null): WorkspaceConfiguration; @@ -12819,7 +13538,7 @@ declare module 'vscode' { * * @param type The task kind type this provider is registered for. * @param provider A task provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerTaskProvider(type: string, provider: TaskProvider): Disposable; @@ -12832,9 +13551,18 @@ declare module 'vscode' { * @param scheme The uri-{@link Uri.scheme scheme} the provider registers for. * @param provider The filesystem provider. * @param options Immutable metadata about the provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ - export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider, options?: { readonly isCaseSensitive?: boolean; readonly isReadonly?: boolean }): Disposable; + export function registerFileSystemProvider(scheme: string, provider: FileSystemProvider, options?: { + /** + * Whether the file system provider use case sensitive compare for {@link Uri.path paths} + */ + readonly isCaseSensitive?: boolean; + /** + * Whether the file system provider is readonly, no modifications like write, delete, create are possible. + */ + readonly isReadonly?: boolean; + }): Disposable; /** * When true, the user has explicitly trusted the contents of the workspace. @@ -12853,7 +13581,16 @@ declare module 'vscode' { * a '{@link TextDocument}' or * a '{@link WorkspaceFolder}' */ - export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { uri?: Uri; languageId: string }; + export type ConfigurationScope = Uri | TextDocument | WorkspaceFolder | { + /** + * The uri of a {@link TextDocument text document} + */ + uri?: Uri; + /** + * The language of a text document + */ + languageId: string; + }; /** * An event describing the change in Configuration @@ -12866,7 +13603,7 @@ declare module 'vscode' { * * @param section Configuration name, supports _dotted_ names. * @param scope A scope in which to check. - * @return `true` if the given section has changed. + * @returns `true` if the given section has changed. */ affectsConfiguration(section: string, scope?: ConfigurationScope): boolean; } @@ -12903,7 +13640,7 @@ declare module 'vscode' { /** * Return the identifiers of all known languages. - * @return Promise resolving to an array of identifier strings. + * @returns Promise resolving to an array of identifier strings. */ export function getLanguages(): Thenable; @@ -12963,7 +13700,7 @@ declare module 'vscode' { * * @param selector A document selector. * @param document A text document. - * @return A number `>0` when the selector matches and `0` when the selector does not match. + * @returns A number `>0` when the selector matches and `0` when the selector does not match. */ export function match(selector: DocumentSelector, document: TextDocument): number; @@ -12992,7 +13729,7 @@ declare module 'vscode' { * Create a diagnostics collection. * * @param name The {@link DiagnosticCollection.name name} of the collection. - * @return A new diagnostic collection. + * @returns A new diagnostic collection. */ export function createDiagnosticCollection(name?: string): DiagnosticCollection; @@ -13001,6 +13738,7 @@ declare module 'vscode' { * * @param id The identifier of the item. * @param selector The document selector that defines for what editors the item shows. + * @returns A new language status item. */ export function createLanguageStatusItem(id: string, selector: DocumentSelector): LanguageStatusItem; @@ -13021,7 +13759,7 @@ declare module 'vscode' { * @param selector A selector that defines the documents this provider is applicable to. * @param provider A completion provider. * @param triggerCharacters Trigger completion when the user types one of the characters. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerCompletionItemProvider(selector: DocumentSelector, provider: CompletionItemProvider, ...triggerCharacters: string[]): Disposable; @@ -13034,7 +13772,7 @@ declare module 'vscode' { * * @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. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerInlineCompletionItemProvider(selector: DocumentSelector, provider: InlineCompletionItemProvider): Disposable; @@ -13048,7 +13786,7 @@ declare module 'vscode' { * @param selector A selector that defines the documents this provider is applicable to. * @param provider A code action provider. * @param metadata Metadata about the kind of code actions the provider provides. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerCodeActionsProvider(selector: DocumentSelector, provider: CodeActionProvider, metadata?: CodeActionProviderMetadata): Disposable; @@ -13061,7 +13799,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A code lens provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerCodeLensProvider(selector: DocumentSelector, provider: CodeLensProvider): Disposable; @@ -13074,7 +13812,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A definition provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDefinitionProvider(selector: DocumentSelector, provider: DefinitionProvider): Disposable; @@ -13087,7 +13825,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider An implementation provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerImplementationProvider(selector: DocumentSelector, provider: ImplementationProvider): Disposable; @@ -13100,7 +13838,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A type definition provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerTypeDefinitionProvider(selector: DocumentSelector, provider: TypeDefinitionProvider): Disposable; @@ -13113,7 +13851,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A declaration provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDeclarationProvider(selector: DocumentSelector, provider: DeclarationProvider): Disposable; @@ -13126,7 +13864,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A hover provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerHoverProvider(selector: DocumentSelector, provider: HoverProvider): Disposable; @@ -13138,7 +13876,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider An evaluatable expression provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerEvaluatableExpressionProvider(selector: DocumentSelector, provider: EvaluatableExpressionProvider): Disposable; @@ -13153,7 +13891,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider An inline values provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerInlineValuesProvider(selector: DocumentSelector, provider: InlineValuesProvider): Disposable; @@ -13166,7 +13904,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document highlight provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDocumentHighlightProvider(selector: DocumentSelector, provider: DocumentHighlightProvider): Disposable; @@ -13180,7 +13918,7 @@ declare module 'vscode' { * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document symbol provider. * @param metaData metadata about the provider - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDocumentSymbolProvider(selector: DocumentSelector, provider: DocumentSymbolProvider, metaData?: DocumentSymbolProviderMetadata): Disposable; @@ -13192,7 +13930,7 @@ declare module 'vscode' { * a failure of the whole operation. * * @param provider A workspace symbol provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerWorkspaceSymbolProvider(provider: WorkspaceSymbolProvider): Disposable; @@ -13205,7 +13943,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A reference provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerReferenceProvider(selector: DocumentSelector, provider: ReferenceProvider): Disposable; @@ -13218,7 +13956,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A rename provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerRenameProvider(selector: DocumentSelector, provider: RenameProvider): Disposable; @@ -13231,7 +13969,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document semantic tokens provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDocumentSemanticTokensProvider(selector: DocumentSelector, provider: DocumentSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; @@ -13250,7 +13988,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document range semantic tokens provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDocumentRangeSemanticTokensProvider(selector: DocumentSelector, provider: DocumentRangeSemanticTokensProvider, legend: SemanticTokensLegend): Disposable; @@ -13263,7 +14001,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document formatting edit provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDocumentFormattingEditProvider(selector: DocumentSelector, provider: DocumentFormattingEditProvider): Disposable; @@ -13280,7 +14018,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document range formatting edit provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDocumentRangeFormattingEditProvider(selector: DocumentSelector, provider: DocumentRangeFormattingEditProvider): Disposable; @@ -13295,7 +14033,7 @@ declare module 'vscode' { * @param provider An on type formatting edit provider. * @param firstTriggerCharacter A character on which formatting should be triggered, like `}`. * @param moreTriggerCharacter More trigger characters. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerOnTypeFormattingEditProvider(selector: DocumentSelector, provider: OnTypeFormattingEditProvider, firstTriggerCharacter: string, ...moreTriggerCharacter: string[]): Disposable; @@ -13309,10 +14047,18 @@ declare module 'vscode' { * @param selector A selector that defines the documents this provider is applicable to. * @param provider A signature help provider. * @param triggerCharacters Trigger signature help when the user types one of the characters, like `,` or `(`. - * @param metadata Information about the provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, ...triggerCharacters: string[]): Disposable; + + /** + * @see {@link languages.registerSignatureHelpProvider} + * + * @param selector A selector that defines the documents this provider is applicable to. + * @param provider A signature help provider. + * @param metadata Information about the provider. + * @returns A {@link Disposable} that unregisters this provider when being disposed. + */ export function registerSignatureHelpProvider(selector: DocumentSelector, provider: SignatureHelpProvider, metadata: SignatureHelpProviderMetadata): Disposable; /** @@ -13324,7 +14070,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A document link provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDocumentLinkProvider(selector: DocumentSelector, provider: DocumentLinkProvider): Disposable; @@ -13337,7 +14083,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A color provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerColorProvider(selector: DocumentSelector, provider: DocumentColorProvider): Disposable; @@ -13350,7 +14096,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider An inlay hints provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerInlayHintsProvider(selector: DocumentSelector, provider: InlayHintsProvider): Disposable; @@ -13367,7 +14113,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A folding range provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerFoldingRangeProvider(selector: DocumentSelector, provider: FoldingRangeProvider): Disposable; @@ -13380,7 +14126,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A selection range provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerSelectionRangeProvider(selector: DocumentSelector, provider: SelectionRangeProvider): Disposable; @@ -13389,7 +14135,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A call hierarchy provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerCallHierarchyProvider(selector: DocumentSelector, provider: CallHierarchyProvider): Disposable; @@ -13398,7 +14144,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A type hierarchy provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerTypeHierarchyProvider(selector: DocumentSelector, provider: TypeHierarchyProvider): Disposable; @@ -13411,7 +14157,7 @@ declare module 'vscode' { * * @param selector A selector that defines the documents this provider is applicable to. * @param provider A linked editing range provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerLinkedEditingRangeProvider(selector: DocumentSelector, provider: LinkedEditingRangeProvider): Disposable; @@ -13421,7 +14167,7 @@ declare module 'vscode' { * @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. + * @returns A {@link Disposable} that unregisters this provider when disposed of. */ export function registerDocumentDropEditProvider(selector: DocumentSelector, provider: DocumentDropEditProvider): Disposable; @@ -13430,7 +14176,7 @@ declare module 'vscode' { * * @param language A language identifier like `typescript`. * @param configuration Language configuration. - * @return A {@link Disposable} that unsets this configuration. + * @returns A {@link Disposable} that unsets this configuration. */ export function setLanguageConfiguration(language: string, configuration: LanguageConfiguration): Disposable; } @@ -13512,7 +14258,13 @@ declare module 'vscode' { * An event that fires when a message is received from a renderer. */ readonly onDidReceiveMessage: Event<{ + /** + * The {@link NotebookEditor editor} that sent the message. + */ readonly editor: NotebookEditor; + /** + * The actual message. + */ readonly message: any; }>; @@ -13648,7 +14400,7 @@ declare module 'vscode' { * Return the cell at the specified index. The index will be adjusted to the notebook. * * @param index - The index of the cell to retrieve. - * @return A {@link NotebookCell cell}. + * @returns A {@link NotebookCell cell}. */ cellAt(index: number): NotebookCell; @@ -13664,7 +14416,7 @@ declare module 'vscode' { /** * Save the document. The saving will be handled by the corresponding {@link NotebookSerializer serializer}. * - * @return A promise that will resolve to true when the document + * @returns A promise that will resolve to true when the document * has been saved. Will return false if the file was not dirty or when save failed. */ save(): Thenable; @@ -13831,7 +14583,16 @@ declare module 'vscode' { /** * The times at which execution started and ended, as unix timestamps */ - readonly timing?: { readonly startTime: number; readonly endTime: number }; + readonly timing?: { + /** + * Execution start time. + */ + readonly startTime: number; + /** + * Execution end time. + */ + readonly endTime: number; + }; } /** @@ -13868,10 +14629,19 @@ declare module 'vscode' { * Derive a new range for this range. * * @param change An object that describes a change to this range. - * @return A range that reflects the given change. Will return `this` range if the change + * @returns A range that reflects the given change. Will return `this` range if the change * is not changing anything. */ - with(change: { start?: number; end?: number }): NotebookRange; + with(change: { + /** + * New start index, defaults to `this.start`. + */ + start?: number; + /** + * New end index, defaults to `this.end`. + */ + end?: number; + }): NotebookRange; } /** @@ -14079,7 +14849,7 @@ declare module 'vscode' { * * @param content Contents of a notebook file. * @param token A cancellation token. - * @return Notebook data or a thenable that resolves to such. + * @returns Notebook data or a thenable that resolves to such. */ deserializeNotebook(content: Uint8Array, token: CancellationToken): NotebookData | Thenable; @@ -14251,7 +15021,16 @@ declare module 'vscode' { * _Note_ that controller selection is persisted (by the controllers {@link NotebookController.id id}) and restored as soon as a * controller is re-created or as a notebook is {@link workspace.onDidOpenNotebookDocument opened}. */ - readonly onDidChangeSelectedNotebooks: Event<{ readonly notebook: NotebookDocument; readonly selected: boolean }>; + readonly onDidChangeSelectedNotebooks: Event<{ + /** + * The notebook for which the controller has been selected or un-selected. + */ + readonly notebook: NotebookDocument; + /** + * Whether the controller has been selected or un-selected. + */ + readonly selected: boolean; + }>; /** * A controller can set affinities for specific notebook documents. This allows a controller @@ -14320,7 +15099,7 @@ declare module 'vscode' { * * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of * this execution. - * @return A thenable that resolves when the operation finished. + * @returns A thenable that resolves when the operation finished. */ clearOutput(cell?: NotebookCell): Thenable; @@ -14330,7 +15109,7 @@ declare module 'vscode' { * @param out Output that replaces the current output. * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of * this execution. - * @return A thenable that resolves when the operation finished. + * @returns A thenable that resolves when the operation finished. */ replaceOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; @@ -14340,7 +15119,7 @@ declare module 'vscode' { * @param out Output that is appended to the current output. * @param cell Cell for which output is cleared. Defaults to the {@link NotebookCellExecution.cell cell} of * this execution. - * @return A thenable that resolves when the operation finished. + * @returns A thenable that resolves when the operation finished. */ appendOutput(out: NotebookCellOutput | readonly NotebookCellOutput[], cell?: NotebookCell): Thenable; @@ -14349,7 +15128,7 @@ declare module 'vscode' { * * @param items Output items that replace the items of existing output. * @param output Output object that already exists. - * @return A thenable that resolves when the operation finished. + * @returns A thenable that resolves when the operation finished. */ replaceOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; @@ -14358,7 +15137,7 @@ declare module 'vscode' { * * @param items Output items that are append to existing output. * @param output Output object that already exists. - * @return A thenable that resolves when the operation finished. + * @returns A thenable that resolves when the operation finished. */ appendOutputItems(items: NotebookCellOutputItem | readonly NotebookCellOutputItem[], output: NotebookCellOutput): Thenable; } @@ -14439,7 +15218,7 @@ declare module 'vscode' { * The provider will be called when the cell scrolls into view, when its content, outputs, language, or metadata change, and when it changes execution state. * @param cell The cell for which to return items. * @param token A token triggered if this request should be cancelled. - * @return One or more {@link NotebookCellStatusBarItem cell statusbar items} + * @returns One or more {@link NotebookCellStatusBarItem cell statusbar items} */ provideCellStatusBarItems(cell: NotebookCell, token: CancellationToken): ProviderResult; } @@ -14462,6 +15241,7 @@ declare module 'vscode' { * @param notebookType A notebook type for which this controller is for. * @param label The label of the controller. * @param handler The execute-handler of the controller. + * @returns A new notebook controller. */ export function createNotebookController(id: string, notebookType: string, label: string, handler?: (cells: NotebookCell[], notebook: NotebookDocument, controller: NotebookController) => void | Thenable): NotebookController; @@ -14470,7 +15250,7 @@ declare module 'vscode' { * * @param notebookType The notebook type to register for. * @param provider A cell status bar provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerNotebookCellStatusBarItemProvider(notebookType: string, provider: NotebookCellStatusBarItemProvider): Disposable; @@ -14513,14 +15293,19 @@ declare module 'vscode' { visible: boolean; } - interface QuickDiffProvider { + /** + * A quick diff provider provides a {@link Uri uri} to the original state of a + * modified resource. The editor will use this information to render ad'hoc diffs + * within the text. + */ + export interface QuickDiffProvider { /** * Provide a {@link Uri} to the original resource of any given resource uri. * * @param uri The uri of the resource open in a text editor. * @param token A cancellation token. - * @return A thenable that resolves to uri of the matching original resource. + * @returns A thenable that resolves to uri of the matching original resource. */ provideOriginalResource?(uri: Uri, token: CancellationToken): ProviderResult; } @@ -14726,6 +15511,9 @@ declare module 'vscode' { dispose(): void; } + /** + * Namespace for source control mangement. + */ export namespace scm { /** @@ -14742,7 +15530,7 @@ declare module 'vscode' { * @param id An `id` for the source control. Something short, e.g.: `git`. * @param label A human-readable string for the source control. E.g.: `Git`. * @param rootUri An optional Uri of the root of the source control. E.g.: `Uri.parse(workspaceRoot)`. - * @return An instance of {@link SourceControl source control}. + * @returns An instance of {@link SourceControl source control}. */ export function createSourceControl(id: string, label: string, rootUri?: Uri): SourceControl; } @@ -14843,7 +15631,7 @@ declare module 'vscode' { * If no DAP breakpoint exists (either because the editor breakpoint was not yet registered or because the debug adapter is not interested in the breakpoint), the value `undefined` is returned. * * @param breakpoint A {@link Breakpoint} in the editor. - * @return A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`. + * @returns A promise that resolves to the Debug Adapter Protocol breakpoint or `undefined`. */ getDebugProtocolBreakpoint(breakpoint: Breakpoint): Thenable; } @@ -14880,7 +15668,7 @@ declare module 'vscode' { * * @param folder The workspace folder for which the configurations are used or `undefined` for a folderless setup. * @param token A cancellation token. - * @return An array of {@link DebugConfiguration debug configurations}. + * @returns An array of {@link DebugConfiguration debug configurations}. */ provideDebugConfigurations?(folder: WorkspaceFolder | undefined, token?: CancellationToken): ProviderResult; @@ -14894,7 +15682,7 @@ declare module 'vscode' { * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. * @param token A cancellation token. - * @return The resolved debug configuration or undefined or null. + * @returns The resolved debug configuration or undefined or null. */ resolveDebugConfiguration?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; @@ -14909,7 +15697,7 @@ declare module 'vscode' { * @param folder The workspace folder from which the configuration originates from or `undefined` for a folderless setup. * @param debugConfiguration The {@link DebugConfiguration debug configuration} to resolve. * @param token A cancellation token. - * @return The resolved debug configuration or undefined or null. + * @returns The resolved debug configuration or undefined or null. */ resolveDebugConfigurationWithSubstitutedVariables?(folder: WorkspaceFolder | undefined, debugConfiguration: DebugConfiguration, token?: CancellationToken): ProviderResult; } @@ -15032,8 +15820,14 @@ declare module 'vscode' { constructor(implementation: DebugAdapter); } + /** + * Represents the different types of debug adapters + */ export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation; + /** + * A debug adaper factory that creates {@link DebugAdapterDescriptor debug adapter descriptors}. + */ export interface DebugAdapterDescriptorFactory { /** * 'createDebugAdapterDescriptor' is called at the start of a debug session to provide details about the debug adapter to use. @@ -15050,7 +15844,7 @@ declare module 'vscode' { * } * @param session The {@link DebugSession debug session} for which the debug adapter will be used. * @param executable The debug adapter's executable information as specified in the package.json (or undefined if no such information exists). - * @return a {@link DebugAdapterDescriptor debug adapter descriptor} or undefined. + * @returns a {@link DebugAdapterDescriptor debug adapter descriptor} or undefined. */ createDebugAdapterDescriptor(session: DebugSession, executable: DebugAdapterExecutable | undefined): ProviderResult; } @@ -15085,13 +15879,16 @@ declare module 'vscode' { onExit?(code: number | undefined, signal: string | undefined): void; } + /** + * A debug adaper factory that creates {@link DebugAdapterTracker debug adapter trackers}. + */ export interface DebugAdapterTrackerFactory { /** * The method 'createDebugAdapterTracker' is called at the start of a debug session in order * to return a "tracker" object that provides read-access to the communication between the editor and a debug adapter. * * @param session The {@link DebugSession debug session} for which the debug adapter tracker will be used. - * @return A {@link DebugAdapterTracker debug adapter tracker} or undefined. + * @returns A {@link DebugAdapterTracker debug adapter tracker} or undefined. */ createDebugAdapterTracker(session: DebugSession): ProviderResult; } @@ -15161,6 +15958,14 @@ declare module 'vscode' { */ readonly logMessage?: string | undefined; + /** + * Creates a new breakpoint + * + * @param enabled Is breakpoint enabled. + * @param condition Expression for conditional breakpoints + * @param hitCondition Expression that controls how many hits of the breakpoint are ignored + * @param logMessage Log message to display when breakpoint is hit + */ protected constructor(enabled?: boolean, condition?: string, hitCondition?: string, logMessage?: string); } @@ -15348,7 +16153,7 @@ declare module 'vscode' { * @param debugType The debug type for which the provider is registered. * @param provider The {@link DebugConfigurationProvider debug configuration provider} to register. * @param triggerKind The {@link DebugConfigurationProviderTriggerKind trigger} for which the 'provideDebugConfiguration' method of the provider is registered. If `triggerKind` is missing, the value `DebugConfigurationProviderTriggerKind.Initial` is assumed. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerDebugConfigurationProvider(debugType: string, provider: DebugConfigurationProvider, triggerKind?: DebugConfigurationProviderTriggerKind): Disposable; @@ -15359,7 +16164,7 @@ declare module 'vscode' { * * @param debugType The debug type for which the factory is registered. * @param factory The {@link DebugAdapterDescriptorFactory debug adapter descriptor factory} to register. - * @return A {@link Disposable} that unregisters this factory when being disposed. + * @returns A {@link Disposable} that unregisters this factory when being disposed. */ export function registerDebugAdapterDescriptorFactory(debugType: string, factory: DebugAdapterDescriptorFactory): Disposable; @@ -15368,7 +16173,7 @@ declare module 'vscode' { * * @param debugType The debug type for which the factory is registered or '*' for matching all debug types. * @param factory The {@link DebugAdapterTrackerFactory debug adapter tracker factory} to register. - * @return A {@link Disposable} that unregisters this factory when being disposed. + * @returns A {@link Disposable} that unregisters this factory when being disposed. */ export function registerDebugAdapterTrackerFactory(debugType: string, factory: DebugAdapterTrackerFactory): Disposable; @@ -15381,13 +16186,15 @@ declare module 'vscode' { * @param folder The {@link WorkspaceFolder workspace folder} for looking up named configurations and resolving variables or `undefined` for a non-folder setup. * @param nameOrConfiguration Either the name of a debug or compound configuration or a {@link DebugConfiguration} object. * @param parentSessionOrOptions Debug session options. When passed a parent {@link DebugSession debug session}, assumes options with just this parent session. - * @return A thenable that resolves when debugging could be successfully started. + * @returns A thenable that resolves when debugging could be successfully started. */ export function startDebugging(folder: WorkspaceFolder | undefined, nameOrConfiguration: string | DebugConfiguration, parentSessionOrOptions?: DebugSession | DebugSessionOptions): Thenable; /** * Stop the given debug session or stop all debug sessions if session is omitted. + * * @param session The {@link DebugSession debug session} to stop; if omitted all sessions are stopped. + * @returns A thenable that resolves when the session(s) have been stopped. */ export function stopDebugging(session?: DebugSession): Thenable; @@ -15412,7 +16219,7 @@ declare module 'vscode' { * * @param source An object conforming to the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol. * @param session An optional debug session that will be used when the source descriptor uses a reference number to load the contents from an active debug session. - * @return A uri that can be used to load the contents of the source. + * @returns A uri that can be used to load the contents of the source. */ export function asDebugSourceUri(source: DebugProtocolSource, session?: DebugSession): Uri; } @@ -15455,7 +16262,7 @@ declare module 'vscode' { * Get an extension by its full identifier in the form of: `publisher.name`. * * @param extensionId An extension identifier. - * @return An extension or `undefined`. + * @returns An extension or `undefined`. */ export function getExtension(extensionId: string): Extension | undefined; @@ -15505,7 +16312,13 @@ declare module 'vscode' { * The state of a comment thread. */ export enum CommentThreadState { + /** + * Unresolved thread state + */ Unresolved = 0, + /** + * Resolved thread state + */ Resolved = 1 } @@ -15773,7 +16586,7 @@ declare module 'vscode' { * * @param id An `id` for the comment controller. * @param label A human-readable string for the comment controller. - * @return An instance of {@link CommentController comment controller}. + * @returns An instance of {@link CommentController comment controller}. */ export function createCommentController(id: string, label: string): CommentController; } @@ -16014,7 +16827,7 @@ declare module 'vscode' { * @param options The {@link AuthenticationGetSessionOptions} to use * @returns A thenable that resolves to an authentication session */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { createIfNone: true }): Thenable; + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** */createIfNone: true }): Thenable; /** * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not @@ -16029,7 +16842,7 @@ declare module 'vscode' { * @param options The {@link AuthenticationGetSessionOptions} to use * @returns A thenable that resolves to an authentication session */ - export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { forceNewSession: true | AuthenticationForceNewSessionOptions }): Thenable; + export function getSession(providerId: string, scopes: readonly string[], options: AuthenticationGetSessionOptions & { /** literal-type defines return type */forceNewSession: true | AuthenticationForceNewSessionOptions }): Thenable; /** * Get an authentication session matching the desired scopes. Rejects if a provider with providerId is not @@ -16062,7 +16875,7 @@ declare module 'vscode' { * @param label The human-readable name of the provider. * @param provider The authentication provider provider. * @param options Additional options for the provider. - * @return A {@link Disposable} that unregisters this provider when being disposed. + * @returns A {@link Disposable} that unregisters this provider when being disposed. */ export function registerAuthenticationProvider(id: string, label: string, provider: AuthenticationProvider, options?: AuthenticationProviderOptions): Disposable; } @@ -16171,8 +16984,17 @@ declare module 'vscode' { * The kind of executions that {@link TestRunProfile TestRunProfiles} control. */ export enum TestRunProfileKind { + /** + * The `Run` test profile kind. + */ Run = 1, + /** + * The `Debug` test profile kind. + */ Debug = 2, + /** + * The `Coverage` test profile kind. + */ Coverage = 3, } @@ -17020,8 +17842,17 @@ declare module 'vscode' { * This is to be used when you can guarantee no identifiable information is contained in the value and the cleaning is improperly redacting it. */ export class TelemetryTrustedValue { + + /** + * The value that is trusted to not contain PII. + */ readonly value: T; + /** + * Creates a new telementry trusted value. + * + * @param value A value to trust + */ constructor(value: T); } @@ -17168,5 +17999,11 @@ interface Thenable { * @returns A Promise for the completion of which ever callback is executed. */ then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => TResult | Thenable): Thenable; + /** + * Attaches callbacks for the resolution and/or rejection of the Promise. + * @param onfulfilled The callback to execute when the Promise is resolved. + * @param onrejected The callback to execute when the Promise is rejected. + * @returns A Promise for the completion of which ever callback is executed. + */ then(onfulfilled?: (value: T) => TResult | Thenable, onrejected?: (reason: any) => void): Thenable; } From e1430e432895252e7cba410898e89d0337c564d1 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 1 Sep 2023 13:41:45 +0200 Subject: [PATCH 432/607] Sets up hot reloading of css and code (requires vscode-diagnostic-tools extension to be installed) --- .vscode/launch.json | 6 +- scripts/debugger-scripts-api.d.ts | 22 ++ scripts/hot-reload-injected-script.js | 267 ++++++++++++++++++ src/vs/base/common/hotReload.ts | 59 ++++ .../browser/widget/diffEditorWidget2/utils.ts | 36 +-- 5 files changed, 368 insertions(+), 22 deletions(-) create mode 100644 scripts/debugger-scripts-api.d.ts create mode 100644 scripts/hot-reload-injected-script.js create mode 100644 src/vs/base/common/hotReload.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 3bea8e7c076..b2e25927a8a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -256,7 +256,11 @@ "browserLaunchLocation": "workspace", "presentation": { "hidden": true, - } + }, + // This is read by the vscode-diagnostic-tools extension + "vscode-diagnostic-tools.debuggerScripts": [ + "${workspaceFolder}/scripts/hot-reload-injected-script.js" + ] }, { "type": "node", diff --git a/scripts/debugger-scripts-api.d.ts b/scripts/debugger-scripts-api.d.ts new file mode 100644 index 00000000000..149912bc04f --- /dev/null +++ b/scripts/debugger-scripts-api.d.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +type RunFunction = ((debugSession: IDebugSession) => IDisposable) | ((debugSession: IDebugSession) => Promise); + +interface IDebugSession { + name: string; + eval(expression: string): Promise; + evalJs(bodyFn: (...args: T) => void, ...args: T): Promise; +} + +interface IDisposable { + dispose(): void; +} + +interface GlobalThisAddition extends globalThis { + $hotReload_applyNewExports?(oldExports: Record): AcceptNewExportsFn | undefined; +} + +type AcceptNewExportsFn = (newExports: Record) => boolean; diff --git a/scripts/hot-reload-injected-script.js b/scripts/hot-reload-injected-script.js new file mode 100644 index 00000000000..c6311f3b9c9 --- /dev/null +++ b/scripts/hot-reload-injected-script.js @@ -0,0 +1,267 @@ +/*--------------------------------------------------------------------------------------------- + * 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 path = require('path'); +const fsPromise = require('fs/promises'); +const parcelWatcher = require('@parcel/watcher'); + +// This file is loaded by the vscode-diagnostic-tools extension and injected into the debugger. + +/** @type {RunFunction} */ +module.exports.run = async function (debugSession) { + const watcher = await DirWatcher.watchRecursively(path.join(__dirname, '../out/')); + + const sub = watcher.onDidChange(changes => { + const supportedChanges = changes.filter(c => c.path.endsWith('.js') || c.path.endsWith('.css')); + debugSession.evalJs(function (changes, debugSessionName) { + // This function is stringified and injected into the debuggee. + + /** @type {{ count: number; originalWindowTitle: any; timeout: any; shouldReload: boolean }} */ + const hotReloadData = globalThis.$hotReloadData || (globalThis.$hotReloadData = { count: 0, messageHideTimeout: undefined, shouldReload: false }); + + /** + * @param {string} path + * @param {string} newSrc + */ + function handleChange(path, newSrc) { + const relativePath = path.replace(/\\/g, '/').split('/out/')[1]; + if (relativePath.endsWith('.css')) { + handleCssChange(relativePath); + } else if (relativePath.endsWith('.js')) { + handleJsChange(relativePath, newSrc); + } + } + + /** + * @param {string} relativePath + */ + function handleCssChange(relativePath) { + if (typeof document === 'undefined') { + return; + } + + const styleSheet = (/** @type {HTMLLinkElement[]} */ ([...document.querySelectorAll(`link[rel='stylesheet']`)])) + .find(l => new URL(l.href, document.location.href).pathname.endsWith(relativePath)); + if (styleSheet) { + setMessage(`reload ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + console.log(debugSessionName, 'css reloaded', relativePath); + styleSheet.href = styleSheet.href.replace(/\?.*/, '') + '?' + Date.now(); + } else { + setMessage(`could not reload ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + console.log(debugSessionName, 'ignoring css change, as stylesheet is not loaded', relativePath); + } + } + + /** + * @param {string} relativePath + * @param {string} newSrc + */ + function handleJsChange(relativePath, newSrc) { + const moduleIdStr = trimEnd(relativePath, '.js'); + + /** @type {any} */ + const requireFn = globalThis.require; + const moduleManager = requireFn.moduleManager; + if (!moduleManager) { + console.log(debugSessionName, 'ignoring js change, as moduleManager is not available', relativePath); + return; + } + + const moduleId = moduleManager._moduleIdProvider.getModuleId(moduleIdStr); + const oldModule = moduleManager._modules2[moduleId]; + + if (!oldModule) { + console.log(debugSessionName, 'ignoring js change, as module is not loaded', relativePath); + return; + } + + // Check if we can reload + const g = /** @type {GlobalThisAddition} */ (globalThis); + + // A frozen copy of the previous exports + const oldExports = Object.freeze({ ...oldModule.exports }); + const reloadFn = g.$hotReload_applyNewExports?.(oldExports); + + if (!reloadFn) { + console.log(debugSessionName, 'ignoring js change, as module does not support hot-reload', relativePath); + hotReloadData.shouldReload = true; + setMessage(`hot reload not supported for ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + return; + } + + const newScript = new Function('define', newSrc); // CodeQL [SM01632] This code is only executed during development. It is required for the hot-reload functionality. + + newScript(/* define */ function (deps, callback) { + // Evaluating the new code was successful. + + // Redefine the module + delete moduleManager._modules2[moduleId]; + moduleManager.defineModule(moduleIdStr, deps, callback); + const newModule = moduleManager._modules2[moduleId]; + + + // Patch the exports of the old module, so that modules using the old module get the new exports + Object.assign(oldModule.exports, newModule.exports); + // We override the exports so that future reloads still patch the initial exports. + newModule.exports = oldModule.exports; + + const successful = reloadFn(newModule.exports); + if (!successful) { + hotReloadData.shouldReload = true; + setMessage(`hot reload failed ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + console.log(debugSessionName, 'hot reload was not successful', relativePath); + return; + } + + console.log(debugSessionName, 'hot reloaded', moduleIdStr); + setMessage(`successfully reloaded ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + }); + } + + /** + * @param {string} message + */ + function setMessage(message) { + const domElem = /** @type {HTMLDivElement | undefined} */ (document.querySelector('.titlebar-center .window-title')); + if (!domElem) { return; } + if (!hotReloadData.timeout) { + hotReloadData.originalWindowTitle = domElem.innerText; + } else { + clearTimeout(hotReloadData.timeout); + } + if (hotReloadData.shouldReload) { + message += ' (manual reload required)'; + } + + domElem.innerText = message; + hotReloadData.timeout = setTimeout(() => { + hotReloadData.timeout = undefined; + // If wanted, we can restore the previous title message + // domElem.replaceChildren(hotReloadData.originalWindowTitle); + }, 5000); + } + + /** + * @param {string} path + * @returns {string} + */ + function formatPath(path) { + const parts = path.split('/'); + parts.reverse(); + let result = parts[0]; + parts.shift(); + for (const p of parts) { + if (result.length + p.length > 40) { + break; + } + result = p + '/' + result; + if (result.length > 20) { + break; + } + } + return result; + } + + function trimEnd(str, suffix) { + if (str.endsWith(suffix)) { + return str.substring(0, str.length - suffix.length); + } + return str; + } + + for (const change of changes) { + handleChange(change.path, change.newContent); + } + + }, supportedChanges, debugSession.name.substring(0, 25)); + }); + + return { + dispose() { + sub.dispose(); + watcher.dispose(); + } + }; +}; + +class DirWatcher { + /** + * + * @param {string} dir + * @returns {Promise} + */ + static async watchRecursively(dir) { + /** @type {((changes: { path: string, newContent: string }[]) => void)[]} */ + const listeners = []; + /** @type {Map } */ + const fileContents = new Map(); + /** @type {Map} */ + const changes = new Map(); + /** @type {(handler: (changes: { path: string, newContent: string }[]) => void) => IDisposable} */ + const event = (handler) => { + listeners.push(handler); + return { + dispose: () => { + const idx = listeners.indexOf(handler); + if (idx >= 0) { + listeners.splice(idx, 1); + } + } + }; + }; + const r = parcelWatcher.subscribe(dir, async (err, events) => { + for (const e of events) { + if (e.type === 'update') { + const newContent = await fsPromise.readFile(e.path, 'utf8'); + if (fileContents.get(e.path) !== newContent) { + fileContents.set(e.path, newContent); + changes.set(e.path, { path: e.path, newContent }); + } + } + } + if (changes.size > 0) { + debounce(() => { + const uniqueChanges = Array.from(changes.values()); + changes.clear(); + listeners.forEach(l => l(uniqueChanges)); + })(); + } + }); + const result = await r; + return new DirWatcher(event, () => result.unsubscribe()); + } + + /** + * @param {(handler: (changes: { path: string, newContent: string }[]) => void) => IDisposable} onDidChange + * @param {() => void} unsub + */ + constructor(onDidChange, unsub) { + this.onDidChange = onDidChange; + this.unsub = unsub; + } + + dispose() { + this.unsub(); + } +} + +/** + * Debounce function calls + * @param {() => void} fn + * @param {number} delay + */ +function debounce(fn, delay = 50) { + let timeoutId; + return function (...args) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + fn.apply(this, args); + }, delay); + }; +} + diff --git a/src/vs/base/common/hotReload.ts b/src/vs/base/common/hotReload.ts new file mode 100644 index 00000000000..17724907937 --- /dev/null +++ b/src/vs/base/common/hotReload.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 { IDisposable } from 'vs/base/common/lifecycle'; +import { env } from 'vs/base/common/process'; + +export function isHotReloadEnabled(): boolean { + return !!env['VSCODE_DEV']; +} +export function registerHotReloadHandler(handler: HotReloadHandler): IDisposable { + if (!isHotReloadEnabled()) { + return { dispose() { } }; + } else { + const handlers = registerGlobalHotReloadHandler(); + + handlers.add(handler); + return { + dispose() { handlers.delete(handler); } + }; + } +} + +/** + * Takes the old exports of the module to reload and returns a function to apply the new exports. + * If `undefined` is returned, this handler is not able to handle the module. + * + * If no handler can apply the new exports, the module will not be reloaded. + */ +export type HotReloadHandler = (oldExports: Record) => AcceptNewExportsHandler | undefined; +export type AcceptNewExportsHandler = (newExports: Record) => boolean; + +function registerGlobalHotReloadHandler() { + if (!hotReloadHandlers) { + hotReloadHandlers = new Set(); + } + + const g = globalThis as unknown as GlobalThisAddition; + if (!g.$hotReload_applyNewExports) { + g.$hotReload_applyNewExports = oldExports => { + for (const h of hotReloadHandlers!) { + const result = h(oldExports); + if (result) { return result; } + } + return undefined; + }; + } + + return hotReloadHandlers; +} + +let hotReloadHandlers: Set<(oldExports: Record) => AcceptNewExportsFn | undefined> | undefined = undefined; + +interface GlobalThisAddition { + $hotReload_applyNewExports?(oldExports: Record): AcceptNewExportsFn | undefined; +} + +type AcceptNewExportsFn = (newExports: Record) => boolean; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts index 8460b24326e..e2326b84447 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDimension } from 'vs/base/browser/dom'; +import { isHotReloadEnabled, registerHotReloadHandler } from 'vs/base/common/hotReload'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; @@ -294,28 +295,21 @@ export function readHotReloadableExport(value: T, reader: IReader | undefined } export function observeHotReloadableExports(values: any[], reader: IReader | undefined): void { - const hotReload_deprecateExports = (globalThis as unknown as { - // This property it defined by the monaco editor playground server - $hotReload_deprecateExports: Set<(oldExports: Record, newExports: Record) => boolean>; - }).$hotReload_deprecateExports; - if (!hotReload_deprecateExports) { - return; + if (isHotReloadEnabled()) { + const o = observableSignalFromEvent( + 'reload', + event => registerHotReloadHandler(oldExports => { + if (![...Object.values(oldExports)].some(v => values.includes(v))) { + return undefined; + } + return (_newExports) => { + event(undefined); + return true; + }; + }) + ); + o.read(reader); } - - const o = observableSignalFromEvent('reload', e => { - function handleExports(oldExports: Record, _newExports: Record) { - if ([...Object.values(oldExports)].some(v => values.includes(v))) { - e(undefined); - return true; - } - return false; - } - hotReload_deprecateExports.add(handleExports); - return { - dispose() { hotReload_deprecateExports.delete(handleExports); } - }; - }); - o.read(reader); } export function applyViewZones(editor: ICodeEditor, viewZones: IObservable, setIsUpdating?: (isUpdatingViewZones: boolean) => void): IDisposable { From e424e83820568bd3a954182020a730fc2972da7a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 1 Sep 2023 12:23:31 +0200 Subject: [PATCH 433/607] Diff Algorithm Cleanup --- src/vs/base/common/arrays.ts | 24 + src/vs/base/common/arraysFind.ts | 109 ++++ src/vs/base/test/common/arraysFind.test.ts | 58 +++ .../browser/services/editorWorkerService.ts | 9 +- .../editor/browser/widget/diffEditorWidget.ts | 16 +- .../diffEditorWidget2/accessibleDiffViewer.ts | 40 +- .../diffEditorDecorations.ts | 30 +- .../diffEditorWidget2/diffEditorViewModel.ts | 15 +- .../diffEditorWidget2/diffEditorWidget2.ts | 58 +-- .../inlineDiffDeletedCodeMargin.ts | 20 +- .../widget/diffEditorWidget2/lineAlignment.ts | 20 +- .../diffEditorWidget2/overviewRulerPart.ts | 4 +- .../widget/workerBasedDocumentDiffProvider.ts | 4 +- src/vs/editor/common/core/lineRange.ts | 222 ++++++--- src/vs/editor/common/core/offsetRange.ts | 8 + .../common/diff/advancedLinesDiffComputer.ts | 465 ++++++------------ .../common/diff/documentDiffProvider.ts | 5 +- .../common/diff/legacyLinesDiffComputer.ts | 27 +- .../editor/common/diff/linesDiffComputer.ts | 142 +----- src/vs/editor/common/diff/rangeMapping.ts | 133 +++++ .../common/services/editorSimpleWorker.ts | 7 +- .../standalone/browser/standaloneEditor.ts | 7 +- .../browser/widget/diffEditorWidget2.test.ts | 8 +- .../editor/test/common/core/lineRange.test.ts | 58 +++ .../diffing/advancedLinesDiffComputer.test.ts | 100 ++++ .../test/node/diffing/diffingFixture.test.ts | 8 +- .../node/diffing/lineRangeMapping.test.ts | 54 -- .../browser/inlineChatLivePreviewWidget.ts | 18 +- .../inlineChat/browser/inlineChatSession.ts | 10 +- .../browser/inlineChatStrategies.ts | 2 +- .../inlineChat/browser/inlineChatWidget.ts | 12 +- .../mergeEditor/browser/model/diffComputer.ts | 6 +- .../mergeEditor/test/browser/model.test.ts | 4 +- 33 files changed, 965 insertions(+), 738 deletions(-) create mode 100644 src/vs/base/common/arraysFind.ts create mode 100644 src/vs/base/test/common/arraysFind.test.ts create mode 100644 src/vs/editor/common/diff/rangeMapping.ts create mode 100644 src/vs/editor/test/common/core/lineRange.test.ts create mode 100644 src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts delete mode 100644 src/vs/editor/test/node/diffing/lineRangeMapping.test.ts diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 94966dc1b6f..71bedc3a440 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -177,6 +177,30 @@ export function groupBy(data: ReadonlyArray, compare: (a: T, b: T) => numb return result; } +/** + * Splits the given items into a list of (non-empty) groups. + * `shouldBeGrouped` is used to decide if two consecutive items should be in the same group. + * The order of the items is preserved. + */ +export function* groupAdjacentBy(items: Iterable, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable { + let currentGroup: T[] | undefined; + let last: T | undefined; + for (const item of items) { + if (last !== undefined && shouldBeGrouped(last, item)) { + currentGroup!.push(item); + } else { + if (currentGroup) { + yield currentGroup; + } + currentGroup = [item]; + } + last = item; + } + if (currentGroup) { + yield currentGroup; + } +} + interface IMutableSplice extends ISplice { readonly toInsert: T[]; deleteCount: number; diff --git a/src/vs/base/common/arraysFind.ts b/src/vs/base/common/arraysFind.ts new file mode 100644 index 00000000000..91a1b710823 --- /dev/null +++ b/src/vs/base/common/arraysFind.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. + *--------------------------------------------------------------------------------------------*/ + +/** + * Finds the last item where predicate is true using binary search. + * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! + * + * @returns `undefined` if no item matches, otherwise the last item that matches the predicate. + */ +export function findLastMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { + const idx = findLastIdxMonotonous(arr, predicate); + return idx === -1 ? undefined : arr[idx]; +} + +/** + * Finds the last item where predicate is true using binary search. + * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! + * + * @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate. + */ +export function findLastIdxMonotonous(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number { + let i = startIdx; + let j = endIdxEx; + while (i < j) { + const k = Math.floor((i + j) / 2); + if (predicate(arr[k])) { + i = k + 1; + } else { + j = k; + } + } + return i - 1; +} + + +/** + * Finds the first item where predicate is true using binary search. + * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`! + * + * @returns `undefined` if no item matches, otherwise the first item that matches the predicate. + */ +export function findFirstMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { + const idx = findFirstIdxMonotonousOrArrLen(arr, predicate); + return idx === arr.length ? undefined : arr[idx]; +} + +/** + * Finds the first item where predicate is true using binary search. + * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`! + * + * @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate. + */ +export function findFirstIdxMonotonousOrArrLen(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number { + let i = startIdx; + let j = endIdxEx; + while (i < j) { + const k = Math.floor((i + j) / 2); + if (predicate(arr[k])) { + j = k; + } else { + i = k + 1; + } + } + return i; +} + +export function findFirstIdxMonotonous(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number { + const idx = findFirstIdxMonotonousOrArrLen(arr, predicate, startIdx, endIdxEx); + return idx === arr.length ? -1 : idx; +} + +/** + * Use this when + * * You have a sorted array + * * You query this array with a monotonous predicate to find the last item that has a certain property. + * * You query this array multiple times with monotonous predicates that get weaker and weaker. + */ +export class MonotonousArray { + public static assertInvariants = false; + + private _findLastMonotonousLastIdx = 0; + private _lastPredicate: ((item: T) => boolean) | undefined; + + constructor(private readonly _items: T[]) { + } + + /** + * The predicate must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! + * For subsequent calls, current predicate must be weaker than (or equal to) the previous predicate, i.e. more entries must be `true`. + */ + findLastMonotonous(predicate: (item: T) => boolean): T | undefined { + if (MonotonousArray.assertInvariants) { + if (this._lastPredicate) { + for (const item of this._items) { + if (this._lastPredicate(item) && !predicate(item)) { + throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.'); + } + } + } + this._lastPredicate = predicate; + } + + const idx = findLastIdxMonotonous(this._items, predicate, this._findLastMonotonousLastIdx); + this._findLastMonotonousLastIdx = idx + 1; + return idx === -1 ? undefined : this._items[idx]; + } +} diff --git a/src/vs/base/test/common/arraysFind.test.ts b/src/vs/base/test/common/arraysFind.test.ts new file mode 100644 index 00000000000..db9fcc44b82 --- /dev/null +++ b/src/vs/base/test/common/arraysFind.test.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert = require('assert'); +import { MonotonousArray, findFirstMonotonous, findLastMonotonous } from 'vs/base/common/arraysFind'; + +suite('Arrays', () => { + test('findLastMonotonous', () => { + const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69]; + + const result = findLastMonotonous(array, n => n <= 60); + assert.strictEqual(result, 60); + + const result2 = findLastMonotonous(array, n => n <= 62); + assert.strictEqual(result2, 61); + + const result3 = findLastMonotonous(array, n => n <= 1); + assert.strictEqual(result3, 1); + + const result4 = findLastMonotonous(array, n => n <= 70); + assert.strictEqual(result4, 69); + + const result5 = findLastMonotonous(array, n => n <= 0); + assert.strictEqual(result5, undefined); + }); + + test('findFirstMonotonous', () => { + const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69]; + + const result = findFirstMonotonous(array, n => n >= 60); + assert.strictEqual(result, 60); + + const result2 = findFirstMonotonous(array, n => n >= 62); + assert.strictEqual(result2, 64); + + const result3 = findFirstMonotonous(array, n => n >= 1); + assert.strictEqual(result3, 1); + + const result4 = findFirstMonotonous(array, n => n >= 70); + assert.strictEqual(result4, undefined); + + const result5 = findFirstMonotonous(array, n => n >= 0); + assert.strictEqual(result5, 1); + }); + + test('MonotonousArray', () => { + const arr = new MonotonousArray([1, 4, 5, 7, 55, 59, 60, 61, 64, 69]); + assert.strictEqual(arr.findLastMonotonous(n => n <= 0), undefined); + assert.strictEqual(arr.findLastMonotonous(n => n <= 0), undefined); + assert.strictEqual(arr.findLastMonotonous(n => n <= 5), 5); + assert.strictEqual(arr.findLastMonotonous(n => n <= 6), 5); + assert.strictEqual(arr.findLastMonotonous(n => n <= 55), 55); + assert.strictEqual(arr.findLastMonotonous(n => n <= 60), 60); + assert.strictEqual(arr.findLastMonotonous(n => n <= 80), 69); + }); +}); diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index 993fe8e813c..5b411eeec3c 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -26,7 +26,8 @@ import { IEditorWorkerHost } from 'vs/editor/common/services/editorWorkerHost'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { IDocumentDiff, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; -import { ILinesDiffComputerOptions, LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { ILinesDiffComputerOptions, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping, RangeMapping, LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { LineRange } from 'vs/editor/common/core/lineRange'; /** @@ -107,15 +108,15 @@ export class EditorWorkerService extends Disposable implements IEditorWorkerServ quitEarly: result.quitEarly, changes: toLineRangeMappings(result.changes), moves: result.moves.map(m => new MovedText( - new SimpleLineRangeMapping(new LineRange(m[0], m[1]), new LineRange(m[2], m[3])), + new LineRangeMapping(new LineRange(m[0], m[1]), new LineRange(m[2], m[3])), toLineRangeMappings(m[4]) )) }; return diff; - function toLineRangeMappings(changes: readonly ILineChange[]): readonly LineRangeMapping[] { + function toLineRangeMappings(changes: readonly ILineChange[]): readonly DetailedLineRangeMapping[] { return changes.map( - (c) => new LineRangeMapping( + (c) => new DetailedLineRangeMapping( new LineRange(c[0], c[1]), new LineRange(c[2], c[3]), c[4]?.map( diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index 62d42cee6aa..47dba6865b7 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -1212,24 +1212,24 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE let modifiedEndLineNumber: number; let innerChanges = m.innerChanges; - if (m.originalRange.isEmpty) { + if (m.original.isEmpty) { // Insertion - originalStartLineNumber = m.originalRange.startLineNumber - 1; + originalStartLineNumber = m.original.startLineNumber - 1; originalEndLineNumber = 0; innerChanges = undefined; } else { - originalStartLineNumber = m.originalRange.startLineNumber; - originalEndLineNumber = m.originalRange.endLineNumberExclusive - 1; + originalStartLineNumber = m.original.startLineNumber; + originalEndLineNumber = m.original.endLineNumberExclusive - 1; } - if (m.modifiedRange.isEmpty) { + if (m.modified.isEmpty) { // Deletion - modifiedStartLineNumber = m.modifiedRange.startLineNumber - 1; + modifiedStartLineNumber = m.modified.startLineNumber - 1; modifiedEndLineNumber = 0; innerChanges = undefined; } else { - modifiedStartLineNumber = m.modifiedRange.startLineNumber; - modifiedEndLineNumber = m.modifiedRange.endLineNumberExclusive - 1; + modifiedStartLineNumber = m.modified.startLineNumber; + modifiedEndLineNumber = m.modified.endLineNumberExclusive - 1; } return { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts index 32f45d85186..ce188250447 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts @@ -21,7 +21,7 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { LineRangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping, LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { ILanguageIdCodec } from 'vs/editor/common/languages'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; @@ -45,7 +45,7 @@ export class AccessibleDiffViewer extends Disposable { private readonly _canClose: IObservable, private readonly _width: IObservable, private readonly _height: IObservable, - private readonly _diffs: IObservable, + private readonly _diffs: IObservable, private readonly _editors: DiffEditorEditors, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { @@ -105,7 +105,7 @@ class ViewModel extends Disposable { = this._currentElementIdx.map((idx, r) => this.currentGroup.read(r)?.lines[idx]); constructor( - private readonly _diffs: IObservable, + private readonly _diffs: IObservable, private readonly _editors: DiffEditorEditors, private readonly _setVisible: (visible: boolean, tx: ITransaction | undefined) => void, public readonly canClose: IObservable, @@ -154,7 +154,7 @@ class ViewModel extends Disposable { // This ensures editor commands (like revert/stage) work const currentViewItem = this.currentElement.read(reader); if (currentViewItem && currentViewItem.type !== LineType.Header) { - const lineNumber = currentViewItem.modifiedLineNumber ?? currentViewItem.diff.modifiedRange.startLineNumber; + const lineNumber = currentViewItem.modifiedLineNumber ?? currentViewItem.diff.modified.startLineNumber; this._editors.modified.setSelection(Range.fromPositions(new Position(lineNumber, 1))); } })); @@ -221,44 +221,44 @@ class ViewModel extends Disposable { const viewElementGroupLineMargin = 3; -function computeViewElementGroups(diffs: LineRangeMapping[], originalLineCount: number, modifiedLineCount: number): ViewElementGroup[] { +function computeViewElementGroups(diffs: DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number): ViewElementGroup[] { const result: ViewElementGroup[] = []; - for (const g of group(diffs, (a, b) => (b.modifiedRange.startLineNumber - a.modifiedRange.endLineNumberExclusive < 2 * viewElementGroupLineMargin))) { + for (const g of group(diffs, (a, b) => (b.modified.startLineNumber - a.modified.endLineNumberExclusive < 2 * viewElementGroupLineMargin))) { const viewElements: ViewElement[] = []; viewElements.push(new HeaderViewElement()); const origFullRange = new LineRange( - Math.max(1, g[0].originalRange.startLineNumber - viewElementGroupLineMargin), - Math.min(g[g.length - 1].originalRange.endLineNumberExclusive + viewElementGroupLineMargin, originalLineCount + 1) + Math.max(1, g[0].original.startLineNumber - viewElementGroupLineMargin), + Math.min(g[g.length - 1].original.endLineNumberExclusive + viewElementGroupLineMargin, originalLineCount + 1) ); const modifiedFullRange = new LineRange( - Math.max(1, g[0].modifiedRange.startLineNumber - viewElementGroupLineMargin), - Math.min(g[g.length - 1].modifiedRange.endLineNumberExclusive + viewElementGroupLineMargin, modifiedLineCount + 1) + Math.max(1, g[0].modified.startLineNumber - viewElementGroupLineMargin), + Math.min(g[g.length - 1].modified.endLineNumberExclusive + viewElementGroupLineMargin, modifiedLineCount + 1) ); forEachAdjacentItems(g, (a, b) => { - const origRange = new LineRange(a ? a.originalRange.endLineNumberExclusive : origFullRange.startLineNumber, b ? b.originalRange.startLineNumber : origFullRange.endLineNumberExclusive); - const modifiedRange = new LineRange(a ? a.modifiedRange.endLineNumberExclusive : modifiedFullRange.startLineNumber, b ? b.modifiedRange.startLineNumber : modifiedFullRange.endLineNumberExclusive); + const origRange = new LineRange(a ? a.original.endLineNumberExclusive : origFullRange.startLineNumber, b ? b.original.startLineNumber : origFullRange.endLineNumberExclusive); + const modifiedRange = new LineRange(a ? a.modified.endLineNumberExclusive : modifiedFullRange.startLineNumber, b ? b.modified.startLineNumber : modifiedFullRange.endLineNumberExclusive); origRange.forEach(origLineNumber => { viewElements.push(new UnchangedLineViewElement(origLineNumber, modifiedRange.startLineNumber + (origLineNumber - origRange.startLineNumber))); }); if (b) { - b.originalRange.forEach(origLineNumber => { + b.original.forEach(origLineNumber => { viewElements.push(new DeletedLineViewElement(b, origLineNumber)); }); - b.modifiedRange.forEach(modifiedLineNumber => { + b.modified.forEach(modifiedLineNumber => { viewElements.push(new AddedLineViewElement(b, modifiedLineNumber)); }); } }); - const modifiedRange = g[0].modifiedRange.join(g[g.length - 1].modifiedRange); - const originalRange = g[0].originalRange.join(g[g.length - 1].originalRange); + const modifiedRange = g[0].modified.join(g[g.length - 1].modified); + const originalRange = g[0].original.join(g[g.length - 1].original); - result.push(new ViewElementGroup(new SimpleLineRangeMapping(modifiedRange, originalRange), viewElements)); + result.push(new ViewElementGroup(new LineRangeMapping(modifiedRange, originalRange), viewElements)); } return result; } @@ -272,7 +272,7 @@ enum LineType { class ViewElementGroup { constructor( - public readonly range: SimpleLineRangeMapping, + public readonly range: LineRangeMapping, public readonly lines: readonly ViewElement[], ) { } } @@ -289,7 +289,7 @@ class DeletedLineViewElement { public readonly modifiedLineNumber = undefined; constructor( - public readonly diff: LineRangeMapping, + public readonly diff: DetailedLineRangeMapping, public readonly originalLineNumber: number, ) { } @@ -301,7 +301,7 @@ class AddedLineViewElement { public readonly originalLineNumber = undefined; constructor( - public readonly diff: LineRangeMapping, + public readonly diff: DetailedLineRangeMapping, public readonly modifiedLineNumber: number, ) { } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts index 9a4b5bcf094..504bfe8a12c 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts @@ -42,45 +42,45 @@ export class DiffEditorDecorations extends Disposable { const modifiedDecorations: IModelDeltaDecoration[] = []; if (!movedTextToCompare) { for (const m of diff.mappings) { - if (!m.lineRangeMapping.originalRange.isEmpty) { - originalDecorations.push({ range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); + if (!m.lineRangeMapping.original.isEmpty) { + originalDecorations.push({ range: m.lineRangeMapping.original.toInclusiveRange()!, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); } - if (!m.lineRangeMapping.modifiedRange.isEmpty) { - modifiedDecorations.push({ range: m.lineRangeMapping.modifiedRange.toInclusiveRange()!, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground }); + if (!m.lineRangeMapping.modified.isEmpty) { + modifiedDecorations.push({ range: m.lineRangeMapping.modified.toInclusiveRange()!, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground }); } - if (m.lineRangeMapping.modifiedRange.isEmpty || m.lineRangeMapping.originalRange.isEmpty) { - if (!m.lineRangeMapping.originalRange.isEmpty) { - originalDecorations.push({ range: m.lineRangeMapping.originalRange.toInclusiveRange()!, options: diffWholeLineDeleteDecoration }); + if (m.lineRangeMapping.modified.isEmpty || m.lineRangeMapping.original.isEmpty) { + if (!m.lineRangeMapping.original.isEmpty) { + originalDecorations.push({ range: m.lineRangeMapping.original.toInclusiveRange()!, options: diffWholeLineDeleteDecoration }); } - if (!m.lineRangeMapping.modifiedRange.isEmpty) { - modifiedDecorations.push({ range: m.lineRangeMapping.modifiedRange.toInclusiveRange()!, options: diffWholeLineAddDecoration }); + if (!m.lineRangeMapping.modified.isEmpty) { + modifiedDecorations.push({ range: m.lineRangeMapping.modified.toInclusiveRange()!, options: diffWholeLineAddDecoration }); } } else { for (const i of m.lineRangeMapping.innerChanges || []) { // Don't show empty markers outside the line range - if (m.lineRangeMapping.originalRange.contains(i.originalRange.startLineNumber)) { + if (m.lineRangeMapping.original.contains(i.originalRange.startLineNumber)) { originalDecorations.push({ range: i.originalRange, options: (i.originalRange.isEmpty() && showEmptyDecorations) ? diffDeleteDecorationEmpty : diffDeleteDecoration }); } - if (m.lineRangeMapping.modifiedRange.contains(i.modifiedRange.startLineNumber)) { + if (m.lineRangeMapping.modified.contains(i.modifiedRange.startLineNumber)) { modifiedDecorations.push({ range: i.modifiedRange, options: (i.modifiedRange.isEmpty() && showEmptyDecorations) ? diffAddDecorationEmpty : diffAddDecoration }); } } } - if (!m.lineRangeMapping.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !movedTextToCompare) { - modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modifiedRange.startLineNumber, 1)), options: arrowRevertChange }); + if (!m.lineRangeMapping.modified.isEmpty && this._options.shouldRenderRevertArrows.read(reader) && !movedTextToCompare) { + modifiedDecorations.push({ range: Range.fromPositions(new Position(m.lineRangeMapping.modified.startLineNumber, 1)), options: arrowRevertChange }); } } } if (movedTextToCompare) { for (const m of movedTextToCompare.changes) { - const fullRangeOriginal = m.originalRange.toInclusiveRange(); + const fullRangeOriginal = m.original.toInclusiveRange(); if (fullRangeOriginal) { originalDecorations.push({ range: fullRangeOriginal, options: renderIndicators ? diffLineDeleteDecorationBackgroundWithIndicator : diffLineDeleteDecorationBackground }); } - const fullRangeModified = m.modifiedRange.toInclusiveRange(); + const fullRangeModified = m.modified.toInclusiveRange(); if (fullRangeModified) { modifiedDecorations.push({ range: fullRangeModified, options: renderIndicators ? diffLineAddDecorationBackgroundWithIndicator : diffLineAddDecorationBackground }); } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 860afa96659..143b1834b66 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -11,7 +11,8 @@ import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidg import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; -import { LineRangeMapping, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; import { TextEditInfo } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper'; @@ -293,7 +294,7 @@ export class DiffState { export class DiffMapping { constructor( - readonly lineRangeMapping: LineRangeMapping, + readonly lineRangeMapping: DetailedLineRangeMapping, ) { /* readonly movedTo: MovedText | undefined, @@ -318,19 +319,19 @@ export class DiffMapping { export class UnchangedRegion { public static fromDiffs( - changes: readonly LineRangeMapping[], + changes: readonly DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number, minHiddenLineCount: number, minContext: number, ): UnchangedRegion[] { - const inversedMappings = LineRangeMapping.inverse(changes, originalLineCount, modifiedLineCount); + const inversedMappings = DetailedLineRangeMapping.inverse(changes, originalLineCount, modifiedLineCount); const result: UnchangedRegion[] = []; for (const mapping of inversedMappings) { - let origStart = mapping.originalRange.startLineNumber; - let modStart = mapping.modifiedRange.startLineNumber; - let length = mapping.originalRange.length; + let origStart = mapping.original.startLineNumber; + let modStart = mapping.modified.startLineNumber; + let length = mapping.original.length; const atStart = origStart === 1 && modStart === 1; const atEnd = origStart + length === originalLineCount + 1 && modStart + length === modifiedLineCount + 1; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 9d5047da806..94af3098934 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -29,7 +29,7 @@ import { IDimension } from 'vs/editor/common/core/dimension'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { EditorType, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -248,8 +248,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { const diffs = model.diff.get()?.mappings; if (!diffs) { return; } const diff = diffs.find(d => - viewZone?.detail.afterLineNumber === d.lineRangeMapping.modifiedRange.startLineNumber - 1 || - d.lineRangeMapping.modifiedRange.startLineNumber === lineNumber + viewZone?.detail.afterLineNumber === d.lineRangeMapping.modified.startLineNumber - 1 || + d.lineRangeMapping.modified.startLineNumber === lineNumber ); if (!diff) { return; } this.revert(diff.lineRangeMapping); @@ -260,10 +260,10 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._register(Event.runAndSubscribe(this._editors.modified.onDidChangeCursorPosition, (e) => { if (e?.reason === CursorChangeReason.Explicit) { - const diff = this._diffModel.get()?.diff.get()?.mappings.find(m => m.lineRangeMapping.modifiedRange.contains(e.position.lineNumber)); - if (diff?.lineRangeMapping.modifiedRange.isEmpty) { + const diff = this._diffModel.get()?.diff.get()?.mappings.find(m => m.lineRangeMapping.modified.contains(e.position.lineNumber)); + if (diff?.lineRangeMapping.modified.isEmpty) { this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, { source: 'diffEditor.cursorPositionChanged' }); - } else if (diff?.lineRangeMapping.originalRange.isEmpty) { + } else if (diff?.lineRangeMapping.original.isEmpty) { this._audioCueService.playAudioCue(AudioCue.diffLineInserted, { source: 'diffEditor.cursorPositionChanged' }); } else if (diff) { this._audioCueService.playAudioCue(AudioCue.diffLineModified, { source: 'diffEditor.cursorPositionChanged' }); @@ -436,7 +436,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { }; } - revert(diff: LineRangeMapping): void { + revert(diff: DetailedLineRangeMapping): void { const model = this._diffModel.get()?.model; if (!model) { return; } @@ -447,8 +447,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { })) : [ { - range: diff.modifiedRange.toExclusiveRange(), - text: model.original.getValueInRange(diff.originalRange.toExclusiveRange()) + range: diff.modified.toExclusiveRange(), + text: model.original.getValueInRange(diff.original.toExclusiveRange()) } ]; @@ -456,8 +456,8 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { } private _goTo(diff: DiffMapping): void { - this._editors.modified.setPosition(new Position(diff.lineRangeMapping.modifiedRange.startLineNumber, 1)); - this._editors.modified.revealRangeInCenter(diff.lineRangeMapping.modifiedRange.toExclusiveRange()); + this._editors.modified.setPosition(new Position(diff.lineRangeMapping.modified.startLineNumber, 1)); + this._editors.modified.revealRangeInCenter(diff.lineRangeMapping.modified.toExclusiveRange()); } goToDiff(target: 'previous' | 'next'): void { @@ -470,15 +470,15 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { let diff: DiffMapping | undefined; if (target === 'next') { - diff = diffs.find(d => d.lineRangeMapping.modifiedRange.startLineNumber > curLineNumber) ?? diffs[0]; + diff = diffs.find(d => d.lineRangeMapping.modified.startLineNumber > curLineNumber) ?? diffs[0]; } else { - diff = findLast(diffs, d => d.lineRangeMapping.modifiedRange.startLineNumber < curLineNumber) ?? diffs[diffs.length - 1]; + diff = findLast(diffs, d => d.lineRangeMapping.modified.startLineNumber < curLineNumber) ?? diffs[diffs.length - 1]; } this._goTo(diff); - if (diff.lineRangeMapping.modifiedRange.isEmpty) { + if (diff.lineRangeMapping.modified.isEmpty) { this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, { source: 'diffEditor.goToDiff' }); - } else if (diff.lineRangeMapping.originalRange.isEmpty) { + } else if (diff.lineRangeMapping.original.isEmpty) { this._audioCueService.playAudioCue(AudioCue.diffLineInserted, { source: 'diffEditor.goToDiff' }); } else if (diff) { this._audioCueService.playAudioCue(AudioCue.diffLineModified, { source: 'diffEditor.goToDiff' }); @@ -564,26 +564,26 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { } } -function translatePosition(posInOriginal: Position, mappings: LineRangeMapping[]): Range { - const mapping = findLast(mappings, m => m.originalRange.startLineNumber <= posInOriginal.lineNumber); +function translatePosition(posInOriginal: Position, mappings: DetailedLineRangeMapping[]): Range { + const mapping = findLast(mappings, m => m.original.startLineNumber <= posInOriginal.lineNumber); if (!mapping) { // No changes before the position return Range.fromPositions(posInOriginal); } - if (mapping.originalRange.endLineNumberExclusive <= posInOriginal.lineNumber) { - const newLineNumber = posInOriginal.lineNumber - mapping.originalRange.endLineNumberExclusive + mapping.modifiedRange.endLineNumberExclusive; + if (mapping.original.endLineNumberExclusive <= posInOriginal.lineNumber) { + const newLineNumber = posInOriginal.lineNumber - mapping.original.endLineNumberExclusive + mapping.modified.endLineNumberExclusive; return Range.fromPositions(new Position(newLineNumber, posInOriginal.column)); } if (!mapping.innerChanges) { // Only for legacy algorithm - return Range.fromPositions(new Position(mapping.modifiedRange.startLineNumber, 1)); + return Range.fromPositions(new Position(mapping.modified.startLineNumber, 1)); } const innerMapping = findLast(mapping.innerChanges, m => m.originalRange.getStartPosition().isBeforeOrEqual(posInOriginal)); if (!innerMapping) { - const newLineNumber = posInOriginal.lineNumber - mapping.originalRange.startLineNumber + mapping.modifiedRange.startLineNumber; + const newLineNumber = posInOriginal.lineNumber - mapping.original.startLineNumber + mapping.modified.startLineNumber; return Range.fromPositions(new Position(newLineNumber, posInOriginal.column)); } @@ -620,24 +620,24 @@ function toLineChanges(state: DiffState): ILineChange[] { let modifiedEndLineNumber: number; let innerChanges = m.innerChanges; - if (m.originalRange.isEmpty) { + if (m.original.isEmpty) { // Insertion - originalStartLineNumber = m.originalRange.startLineNumber - 1; + originalStartLineNumber = m.original.startLineNumber - 1; originalEndLineNumber = 0; innerChanges = undefined; } else { - originalStartLineNumber = m.originalRange.startLineNumber; - originalEndLineNumber = m.originalRange.endLineNumberExclusive - 1; + originalStartLineNumber = m.original.startLineNumber; + originalEndLineNumber = m.original.endLineNumberExclusive - 1; } - if (m.modifiedRange.isEmpty) { + if (m.modified.isEmpty) { // Deletion - modifiedStartLineNumber = m.modifiedRange.startLineNumber - 1; + modifiedStartLineNumber = m.modified.startLineNumber - 1; modifiedEndLineNumber = 0; innerChanges = undefined; } else { - modifiedStartLineNumber = m.modifiedRange.startLineNumber; - modifiedEndLineNumber = m.modifiedRange.endLineNumberExclusive - 1; + modifiedStartLineNumber = m.modified.startLineNumber; + modifiedEndLineNumber = m.modified.endLineNumberExclusive - 1; } return { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts b/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts index 5aca1405d41..f5b3ff67e26 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts @@ -13,7 +13,7 @@ import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrow import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -39,7 +39,7 @@ export class InlineDiffDeletedCodeMargin extends Disposable { private readonly _getViewZoneId: () => string, private readonly _marginDomNode: HTMLElement, private readonly _modifiedEditor: CodeEditorWidget, - private readonly _diff: LineRangeMapping, + private readonly _diff: DetailedLineRangeMapping, private readonly _editor: DiffEditorWidget2, private readonly _viewLineCounts: number[], private readonly _originalTextModel: ITextModel, @@ -70,38 +70,38 @@ export class InlineDiffDeletedCodeMargin extends Disposable { getAnchor: () => ({ x, y }), getActions: () => { const actions: Action[] = []; - const isDeletion = _diff.modifiedRange.isEmpty; + const isDeletion = _diff.modified.isEmpty; // default action actions.push(new Action( 'diff.clipboard.copyDeletedContent', isDeletion - ? (_diff.originalRange.length > 1 + ? (_diff.original.length > 1 ? localize('diff.clipboard.copyDeletedLinesContent.label', "Copy deleted lines") : localize('diff.clipboard.copyDeletedLinesContent.single.label', "Copy deleted line")) - : (_diff.originalRange.length > 1 + : (_diff.original.length > 1 ? localize('diff.clipboard.copyChangedLinesContent.label', "Copy changed lines") : localize('diff.clipboard.copyChangedLinesContent.single.label', "Copy changed line")), undefined, true, async () => { - const originalText = this._originalTextModel.getValueInRange(_diff.originalRange.toExclusiveRange()); + const originalText = this._originalTextModel.getValueInRange(_diff.original.toExclusiveRange()); await this._clipboardService.writeText(originalText); } )); - if (_diff.originalRange.length > 1) { + if (_diff.original.length > 1) { actions.push(new Action( 'diff.clipboard.copyDeletedLineContent', isDeletion ? localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", - _diff.originalRange.startLineNumber + currentLineNumberOffset) + _diff.original.startLineNumber + currentLineNumberOffset) : localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", - _diff.originalRange.startLineNumber + currentLineNumberOffset), + _diff.original.startLineNumber + currentLineNumberOffset), undefined, true, async () => { - let lineContent = this._originalTextModel.getLineContent(_diff.originalRange.startLineNumber + currentLineNumberOffset); + let lineContent = this._originalTextModel.getLineContent(_diff.original.startLineNumber + currentLineNumberOffset); if (lineContent === '') { // empty line -> new line const eof = this._originalTextModel.getEndOfLineSequence(); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts index 56357753f62..32d34563d46 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts @@ -25,7 +25,7 @@ import { animatedObservable, joinCombine } from 'vs/editor/browser/widget/diffEd import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { BackgroundTokenizationState } from 'vs/editor/common/tokenizationTextModelPart'; import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel'; @@ -193,7 +193,7 @@ export class ViewZoneManager extends Disposable { const decorations: InlineDecoration[] = []; for (const i of a.diff.innerChanges || []) { decorations.push(new InlineDecoration( - i.originalRange.delta(-(a.diff.originalRange.startLineNumber - 1)), + i.originalRange.delta(-(a.diff.original.startLineNumber - 1)), diffDeleteDecoration.className!, InlineDecorationType.Regular )); @@ -287,7 +287,7 @@ export class ViewZoneManager extends Disposable { } let marginDomNode: HTMLElement | undefined = undefined; - if (a.diff && a.diff.modifiedRange.isEmpty && this._options.shouldRenderRevertArrows.read(reader)) { + if (a.diff && a.diff.modified.isEmpty && this._options.shouldRenderRevertArrows.read(reader)) { marginDomNode = createViewZoneMarginArrow(); } @@ -472,7 +472,7 @@ interface ILineRangeAlignment { * If this range alignment is a direct result of a diff, then this is the diff's line mapping. * Only used for inline-view. */ - diff?: LineRangeMapping; + diff?: DetailedLineRangeMapping; } function computeRangeAlignment( @@ -540,11 +540,11 @@ function computeRangeAlignment( for (const m of diffs) { const c = m.lineRangeMapping; - handleAlignmentsOutsideOfDiffs(c.originalRange.startLineNumber, c.modifiedRange.startLineNumber); + handleAlignmentsOutsideOfDiffs(c.original.startLineNumber, c.modified.startLineNumber); let first = true; - let lastModLineNumber = c.modifiedRange.startLineNumber; - let lastOrigLineNumber = c.originalRange.startLineNumber; + let lastModLineNumber = c.modified.startLineNumber; + let lastOrigLineNumber = c.original.startLineNumber; function emitAlignment(origLineNumberExclusive: number, modLineNumberExclusive: number) { if (origLineNumberExclusive < lastOrigLineNumber || modLineNumberExclusive < lastModLineNumber) { @@ -593,10 +593,10 @@ function computeRangeAlignment( } } - emitAlignment(c.originalRange.endLineNumberExclusive, c.modifiedRange.endLineNumberExclusive); + emitAlignment(c.original.endLineNumberExclusive, c.modified.endLineNumberExclusive); - lastOriginalLineNumber = c.originalRange.endLineNumberExclusive; - lastModifiedLineNumber = c.modifiedRange.endLineNumberExclusive; + lastOriginalLineNumber = c.original.endLineNumberExclusive; + lastModifiedLineNumber = c.modified.endLineNumberExclusive; } handleAlignmentsOutsideOfDiffs(Number.MAX_VALUE, Number.MAX_VALUE); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts b/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts index 8996560b36a..bd93df81567 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts @@ -127,8 +127,8 @@ export class OverviewRulerPart extends Disposable { }); } - const originalZones = createZones((diff || []).map(d => d.lineRangeMapping.originalRange), colors.removeColor, this._editors.original); - const modifiedZones = createZones((diff || []).map(d => d.lineRangeMapping.modifiedRange), colors.insertColor, this._editors.modified); + const originalZones = createZones((diff || []).map(d => d.lineRangeMapping.original), colors.removeColor, this._editors.original); + const modifiedZones = createZones((diff || []).map(d => d.lineRangeMapping.modified), colors.insertColor, this._editors.modified); originalOverviewRuler?.setZones(originalZones); modifiedOverviewRuler?.setZones(modifiedZones); })); diff --git a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts index 02d3c157aa6..79d7bacac9f 100644 --- a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts +++ b/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts @@ -9,7 +9,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { StopWatch } from 'vs/base/common/stopwatch'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { IDocumentDiff, IDocumentDiffProvider, IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; -import { LineRangeMapping, RangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { ITextModel } from 'vs/editor/common/model'; import { DiffAlgorithmName, IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -53,7 +53,7 @@ export class WorkerBasedDocumentDiffProvider implements IDocumentDiffProvider, I return { changes: [ - new LineRangeMapping( + new DetailedLineRangeMapping( new LineRange(1, 2), new LineRange(1, modified.getLineCount() + 1), [ diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index 70acb0476d6..3bf932cac4d 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -6,6 +6,7 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { Range } from 'vs/editor/common/core/range'; +import { findFirstIdxMonotonousOrArrLen, findLastIdxMonotonous, findLastMonotonous } from 'vs/base/common/arraysFind'; /** * A range of lines (1-based). @@ -40,66 +41,11 @@ export class LineRange { if (lineRanges.length === 0) { return []; } - let result = lineRanges[0]; + let result = new LineRangeSet(lineRanges[0].slice()); for (let i = 1; i < lineRanges.length; i++) { - result = this.join(result, lineRanges[i]); + result = result.getUnion(new LineRangeSet(lineRanges[i].slice())); } - return result; - } - - /** - * @param lineRanges1 Must be sorted. - * @param lineRanges2 Must be sorted. - */ - public static join(lineRanges1: readonly LineRange[], lineRanges2: readonly LineRange[]): readonly LineRange[] { - if (lineRanges1.length === 0) { - return lineRanges2; - } - if (lineRanges2.length === 0) { - return lineRanges1; - } - - const result: LineRange[] = []; - let i1 = 0; - let i2 = 0; - let current: LineRange | null = null; - while (i1 < lineRanges1.length || i2 < lineRanges2.length) { - let next: LineRange | null = null; - if (i1 < lineRanges1.length && i2 < lineRanges2.length) { - const lineRange1 = lineRanges1[i1]; - const lineRange2 = lineRanges2[i2]; - if (lineRange1.startLineNumber < lineRange2.startLineNumber) { - next = lineRange1; - i1++; - } else { - next = lineRange2; - i2++; - } - } else if (i1 < lineRanges1.length) { - next = lineRanges1[i1]; - i1++; - } else { - next = lineRanges2[i2]; - i2++; - } - - if (current === null) { - current = next; - } else { - if (current.endLineNumberExclusive >= next.startLineNumber) { - // merge - current = new LineRange(current.startLineNumber, Math.max(current.endLineNumberExclusive, next.endLineNumberExclusive)); - } else { - // push - result.push(current); - current = next; - } - } - } - if (current !== null) { - result.push(current); - } - return result; + return result.ranges; } public static ofLength(startLineNumber: number, length: number): LineRange { @@ -251,3 +197,163 @@ export class LineRange { } export type ISerializedLineRange = [startLineNumber: number, endLineNumberExclusive: number]; + + +export class LineRangeSet { + constructor( + /** + * Sorted by start line number. + * No two line ranges are touching or intersecting. + */ + private readonly _normalizedRanges: LineRange[] = [] + ) { + } + + get ranges(): readonly LineRange[] { + return this._normalizedRanges; + } + + addRange(range: LineRange): void { + if (range.length === 0) { + return; + } + + // Idea: Find joinRange such that: + // replaceRange = _normalizedRanges.replaceRange(joinRange, range.joinAll(joinRange.map(idx => this._normalizedRanges[idx]))) + + // idx of first element that touches range or that is after range + const joinRangeStartIdx = findFirstIdxMonotonousOrArrLen(this._normalizedRanges, r => r.endLineNumberExclusive >= range.startLineNumber); + // idx of element after { last element that touches range or that is before range } + const joinRangeEndIdxExclusive = findLastIdxMonotonous(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; + + if (joinRangeStartIdx === joinRangeEndIdxExclusive) { + // If there is no element that touches range, then joinRangeStartIdx === joinRangeEndIdxExclusive and that value is the index of the element after range + this._normalizedRanges.splice(joinRangeStartIdx, 0, range); + } else if (joinRangeStartIdx === joinRangeEndIdxExclusive - 1) { + // Else, there is an element that touches range and in this case it is both the first and last element. Thus we can replace it + const joinRange = this._normalizedRanges[joinRangeStartIdx]; + this._normalizedRanges[joinRangeStartIdx] = joinRange.join(range); + } else { + // First and last element are different - we need to replace the entire range + const joinRange = this._normalizedRanges[joinRangeStartIdx].join(this._normalizedRanges[joinRangeEndIdxExclusive - 1]).join(range); + this._normalizedRanges.splice(joinRangeStartIdx, joinRangeEndIdxExclusive - joinRangeStartIdx, joinRange); + } + } + + intersects(range: LineRange): boolean { + const rangeThatStartsBeforeEnd = findLastMonotonous(this._normalizedRanges, r => r.startLineNumber < range.endLineNumberExclusive); + return !!rangeThatStartsBeforeEnd && rangeThatStartsBeforeEnd.endLineNumberExclusive > range.startLineNumber; + } + + getUnion(other: LineRangeSet): LineRangeSet { + if (this._normalizedRanges.length === 0) { + return other; + } + if (other._normalizedRanges.length === 0) { + return this; + } + + const result: LineRange[] = []; + let i1 = 0; + let i2 = 0; + let current: LineRange | null = null; + while (i1 < this._normalizedRanges.length || i2 < other._normalizedRanges.length) { + let next: LineRange | null = null; + if (i1 < this._normalizedRanges.length && i2 < other._normalizedRanges.length) { + const lineRange1 = this._normalizedRanges[i1]; + const lineRange2 = other._normalizedRanges[i2]; + if (lineRange1.startLineNumber < lineRange2.startLineNumber) { + next = lineRange1; + i1++; + } else { + next = lineRange2; + i2++; + } + } else if (i1 < this._normalizedRanges.length) { + next = this._normalizedRanges[i1]; + i1++; + } else { + next = other._normalizedRanges[i2]; + i2++; + } + + if (current === null) { + current = next; + } else { + if (current.endLineNumberExclusive >= next.startLineNumber) { + // merge + current = new LineRange(current.startLineNumber, Math.max(current.endLineNumberExclusive, next.endLineNumberExclusive)); + } else { + // push + result.push(current); + current = next; + } + } + } + if (current !== null) { + result.push(current); + } + return new LineRangeSet(result); + } + + /** + * Subtracts all ranges in this set from `range` and returns the result. + */ + subtractFrom(range: LineRange): LineRangeSet { + // idx of first element that touches range or that is after range + const joinRangeStartIdx = findFirstIdxMonotonousOrArrLen(this._normalizedRanges, r => r.endLineNumberExclusive >= range.startLineNumber); + // idx of element after { last element that touches range or that is before range } + const joinRangeEndIdxExclusive = findLastIdxMonotonous(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; + + if (joinRangeStartIdx === joinRangeEndIdxExclusive) { + return new LineRangeSet([range]); + } + + const result: LineRange[] = []; + let startLineNumber = range.startLineNumber; + for (let i = joinRangeStartIdx; i < joinRangeEndIdxExclusive; i++) { + const r = this._normalizedRanges[i]; + if (r.startLineNumber > startLineNumber) { + result.push(new LineRange(startLineNumber, r.startLineNumber)); + } + startLineNumber = r.endLineNumberExclusive; + } + if (startLineNumber < range.endLineNumberExclusive) { + result.push(new LineRange(startLineNumber, range.endLineNumberExclusive)); + } + + return new LineRangeSet(result); + } + + toString() { + return this._normalizedRanges.map(r => r.toString()).join(', '); + } + + getIntersection(other: LineRangeSet): LineRangeSet { + const result: LineRange[] = []; + + let i1 = 0; + let i2 = 0; + while (i1 < this._normalizedRanges.length && i2 < other._normalizedRanges.length) { + const r1 = this._normalizedRanges[i1]; + const r2 = other._normalizedRanges[i2]; + + const i = r1.intersect(r2); + if (i && !i.isEmpty) { + result.push(i); + } + + if (r1.endLineNumberExclusive < r2.endLineNumberExclusive) { + i1++; + } else { + i2++; + } + } + + return new LineRangeSet(result); + } + + getWithDelta(value: number): LineRangeSet { + return new LineRangeSet(this._normalizedRanges.map(r => r.delta(value))); + } +} diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index 27e60bca2df..9173e5339bb 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -136,6 +136,14 @@ export class OffsetRange { } return value; } + + public map(f: (offset: number) => T): T[] { + const result: T[] = []; + for (let i = this.start; i < this.endExclusive; i++) { + result.push(f(i)); + } + return result; + } } export class OffsetRangeSet { diff --git a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts index 55166f1ad82..8720d8524ba 100644 --- a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts @@ -3,12 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Comparator, CompareResult, compareBy, equals, findLastIndex, numberComparator, reverseOrder } from 'vs/base/common/arrays'; +import { compareBy, equals, groupAdjacentBy, numberComparator, pushMany, reverseOrder } from 'vs/base/common/arrays'; import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; import { CharCode } from 'vs/base/common/charCode'; import { SetMap } from 'vs/base/common/collections'; -import { BugIndicatingError } from 'vs/base/common/errors'; -import { LineRange } from 'vs/editor/common/core/lineRange'; +import { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -16,7 +15,9 @@ import { DateTimeout, ISequence, ITimeout, InfiniteTimeout, SequenceDiff } from import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/algorithms/dynamicProgrammingDiffing'; import { optimizeSequenceDiffs, removeRandomLineMatches, removeRandomMatches, smoothenSequenceDiffs } from 'vs/editor/common/diff/algorithms/joinSequenceDiffs'; import { MyersDiffAlgorithm } from 'vs/editor/common/diff/algorithms/myersDiffAlgorithm'; -import { ILinesDiffComputer, ILinesDiffComputerOptions, LineRangeMapping, LinesDiff, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from './rangeMapping'; +import { MonotonousArray, findLastIdxMonotonous, findLastMonotonous, findFirstMonotonous } from 'vs/base/common/arraysFind'; export class AdvancedLinesDiffComputer implements ILinesDiffComputer { private readonly dynamicProgrammingDiffing = new DynamicProgrammingDiffing(); @@ -29,7 +30,7 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { if (originalLines.length === 1 && originalLines[0].length === 0 || modifiedLines.length === 1 && modifiedLines[0].length === 0) { return new LinesDiff([ - new LineRangeMapping( + new DetailedLineRangeMapping( new LineRange(1, originalLines.length + 1), new LineRange(1, modifiedLines.length + 1), [ @@ -167,7 +168,7 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { validatePosition(ic.originalRange.getStartPosition(), originalLines) && validatePosition(ic.originalRange.getEndPosition(), originalLines); if (!valid) { return false; } } - if (!validateRange(c.modifiedRange, modifiedLines) || !validateRange(c.originalRange, originalLines)) { + if (!validateRange(c.modified, modifiedLines) || !validateRange(c.original, originalLines)) { return false; } } @@ -177,16 +178,94 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { return new LinesDiff(changes, moves, hitTimeout); } - private computeMoves(changes: LineRangeMapping[], originalLines: string[], modifiedLines: string[], hashedOriginalLines: number[], hashedModifiedLines: number[], timeout: ITimeout, considerWhitespaceChanges: boolean): MovedText[] { - const moves: SimpleLineRangeMapping[] = []; - const deletions = changes - .filter(c => c.modifiedRange.isEmpty && c.originalRange.length >= 3) - .map(d => new LineRangeFragment(d.originalRange, originalLines, d)); - const insertions = new Set(changes - .filter(c => c.originalRange.isEmpty && c.modifiedRange.length >= 3) - .map(d => new LineRangeFragment(d.modifiedRange, modifiedLines, d))); + private computeMoves( + changes: DetailedLineRangeMapping[], + originalLines: string[], + modifiedLines: string[], + hashedOriginalLines: number[], + hashedModifiedLines: number[], + timeout: ITimeout, + considerWhitespaceChanges: boolean, + ): MovedText[] { + const { moves, excludedChanges } = this.computeMovesFromSimpleDeletionsToSimpleInsertions(changes, originalLines, modifiedLines, timeout); - const excludedChanges = new Set(); + if (!timeout.isValid()) { return []; } + + const unchangedMoves = this.computeUnchangedMoves( + changes.filter(c => !excludedChanges.has(c)), + hashedOriginalLines, + hashedModifiedLines, + timeout + ); + pushMany(moves, unchangedMoves); + + // join moves + moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); + if (moves.length === 0) { + return []; + } + let joinedMoves = [moves[0]]; + for (let i = 1; i < moves.length; i++) { + const last = joinedMoves[joinedMoves.length - 1]; + const current = moves[i]; + + const originalDist = current.original.startLineNumber - last.original.endLineNumberExclusive; + const modifiedDist = current.modified.startLineNumber - last.modified.endLineNumberExclusive; + const currentMoveAfterLast = originalDist >= 0 && modifiedDist >= 0; + + if (currentMoveAfterLast && originalDist + modifiedDist <= 2) { + joinedMoves[joinedMoves.length - 1] = last.join(current); + continue; + } + + const originalText = current.original.toOffsetRange().slice(originalLines).map(l => l.trim()).join('\n'); + if (originalText.length <= 10) { + // Ignore small moves + continue; + } + joinedMoves.push(current); + } + + // Ignore non moves + const changesMonotonous = new MonotonousArray(changes); + joinedMoves = joinedMoves.filter(m => { + const diffBeforeOriginalMove = changesMonotonous.findLastMonotonous(c => c.original.endLineNumberExclusive <= m.original.startLineNumber) + || new LineRangeMapping(new LineRange(1, 1), new LineRange(1, 1)); + + const modifiedDistToPrevDiff = m.modified.startLineNumber - diffBeforeOriginalMove.modified.endLineNumberExclusive; + const originalDistToPrevDiff = m.original.startLineNumber - diffBeforeOriginalMove.original.endLineNumberExclusive; + + const differentDistances = modifiedDistToPrevDiff !== originalDistToPrevDiff; + return differentDistances; + }); + + const movesWithDiffs = joinedMoves.map(m => { + const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( + m.original.toOffsetRange(), + m.modified.toOffsetRange(), + ), timeout, considerWhitespaceChanges); + const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); + return new MovedText(m, mappings); + }); + return movesWithDiffs; + } + + private computeMovesFromSimpleDeletionsToSimpleInsertions( + changes: DetailedLineRangeMapping[], + originalLines: string[], + modifiedLines: string[], + timeout: ITimeout, + ) { + const moves: LineRangeMapping[] = []; + + const deletions = changes + .filter(c => c.modified.isEmpty && c.original.length >= 3) + .map(d => new LineRangeFragment(d.original, originalLines, d)); + const insertions = new Set(changes + .filter(c => c.original.isEmpty && c.modified.length >= 3) + .map(d => new LineRangeFragment(d.modified, modifiedLines, d))); + + const excludedChanges = new Set(); for (const deletion of deletions) { let highestSimilarity = -1; @@ -201,24 +280,31 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { if (highestSimilarity > 0.90 && best) { insertions.delete(best); - moves.push(new SimpleLineRangeMapping(deletion.range, best.range)); + moves.push(new LineRangeMapping(deletion.range, best.range)); excludedChanges.add(deletion.source); excludedChanges.add(best.source); } if (!timeout.isValid()) { - return []; + return { moves, excludedChanges }; } } + return { moves, excludedChanges }; + } + + private computeUnchangedMoves( + changes: DetailedLineRangeMapping[], + hashedOriginalLines: number[], + hashedModifiedLines: number[], + timeout: ITimeout, + ) { + const moves: LineRangeMapping[] = []; + const original3LineHashes = new SetMap(); for (const change of changes) { - if (excludedChanges.has(change)) { - continue; - } - - for (let i = change.originalRange.startLineNumber; i < change.originalRange.endLineNumberExclusive - 2; i++) { + for (let i = change.original.startLineNumber; i < change.original.endLineNumberExclusive - 2; i++) { const key = `${hashedOriginalLines[i - 1]}:${hashedOriginalLines[i + 1 - 1]}:${hashedOriginalLines[i + 2 - 1]}`; original3LineHashes.add(key, { range: new LineRange(i, i + 3) }); } @@ -231,15 +317,11 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { const possibleMappings: PossibleMapping[] = []; - changes.sort(compareBy(c => c.modifiedRange.startLineNumber, numberComparator)); + changes.sort(compareBy(c => c.modified.startLineNumber, numberComparator)); for (const change of changes) { - if (excludedChanges.has(change)) { - continue; - } - let lastMappings: PossibleMapping[] = []; - for (let i = change.modifiedRange.startLineNumber; i < change.modifiedRange.endLineNumberExclusive - 2; i++) { + for (let i = change.modified.startLineNumber; i < change.modified.endLineNumberExclusive - 2; i++) { const key = `${hashedModifiedLines[i - 1]}:${hashedModifiedLines[i + 1 - 1]}:${hashedModifiedLines[i + 2 - 1]}`; const currentModifiedRange = new LineRange(i, i + 3); @@ -280,73 +362,25 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { const diffOrigToMod = mapping.modifiedLineRange.startLineNumber - mapping.originalLineRange.startLineNumber; const modifiedSections = modifiedSet.subtractFrom(mapping.modifiedLineRange); - const originalTranslatedSections = originalSet.subtractFrom(mapping.originalLineRange).map(r => r.delta(diffOrigToMod)); + const originalTranslatedSections = originalSet.subtractFrom(mapping.originalLineRange).getWithDelta(diffOrigToMod); - const modifiedIntersectedSections = intersectRanges(modifiedSections, originalTranslatedSections); + const modifiedIntersectedSections = modifiedSections.getIntersection(originalTranslatedSections); - for (const s of modifiedIntersectedSections) { + for (const s of modifiedIntersectedSections.ranges) { if (s.length < 3) { continue; } const modifiedLineRange = s; const originalLineRange = s.delta(-diffOrigToMod); - moves.push(new SimpleLineRangeMapping(originalLineRange, modifiedLineRange)); + moves.push(new LineRangeMapping(originalLineRange, modifiedLineRange)); modifiedSet.addRange(modifiedLineRange); originalSet.addRange(originalLineRange); } } - // join moves - moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); - if (moves.length === 0) { - return []; - } - let joinedMoves = [moves[0]]; - for (let i = 1; i < moves.length; i++) { - const last = joinedMoves[joinedMoves.length - 1]; - const current = moves[i]; - - const originalDist = current.original.startLineNumber - last.original.endLineNumberExclusive; - const modifiedDist = current.modified.startLineNumber - last.modified.endLineNumberExclusive; - const currentMoveAfterLast = originalDist >= 0 && modifiedDist >= 0; - - if (currentMoveAfterLast && originalDist + modifiedDist <= 2) { - joinedMoves[joinedMoves.length - 1] = last.join(current); - continue; - } - - const originalText = current.original.toOffsetRange().slice(originalLines).map(l => l.trim()).join('\n'); - if (originalText.length <= 10) { - // Ignore small moves - continue; - } - joinedMoves.push(current); - } - - // Ignore non moves - const originalChanges = MonotonousFinder.createOfSorted(changes, c => c.originalRange.endLineNumberExclusive, numberComparator); - joinedMoves = joinedMoves.filter(m => { - const diffBeforeOriginalMove = originalChanges.findLastItemBeforeOrEqual(m.original.startLineNumber) - || new LineRangeMapping(new LineRange(1, 1), new LineRange(1, 1), []); - - const modifiedDistToPrevDiff = m.modified.startLineNumber - diffBeforeOriginalMove.modifiedRange.endLineNumberExclusive; - const originalDistToPrevDiff = m.original.startLineNumber - diffBeforeOriginalMove.originalRange.endLineNumberExclusive; - - const differentDistances = modifiedDistToPrevDiff !== originalDistToPrevDiff; - return differentDistances; - }); - - const fullMoves = joinedMoves.map(m => { - const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( - m.original.toOffsetRange(), - m.modified.toOffsetRange(), - ), timeout, considerWhitespaceChanges); - const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); - return new MovedText(m, mappings); - }); - return fullMoves; + return moves; } private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } { @@ -380,154 +414,6 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { } } -class MonotonousFinder { - public static create( - items: TItem[], - itemToDomain: (item: TItem) => TDomain, - domainComparator: Comparator, - ): MonotonousFinder { - items.sort((a, b) => domainComparator(itemToDomain(a), itemToDomain(b))); - return new MonotonousFinder(items, itemToDomain, domainComparator); - } - - public static createOfSorted( - items: TItem[], - itemToDomain: (item: TItem) => TDomain, - domainComparator: Comparator, - ): MonotonousFinder { - return new MonotonousFinder(items, itemToDomain, domainComparator); - } - - private _currentIdx = 0; // All values with index lower than this are smaller than or equal to _lastValue and vice versa. - private _lastValue: TDomain | undefined = undefined; // Represents a smallest value. - private _hasLastValue = false; - - private constructor( - private readonly _items: TItem[], - private readonly _itemToDomain: (item: TItem) => TDomain, - private readonly _domainComparator: Comparator, - ) { - } - - /** - * Assumes the values are monotonously increasing. - */ - findLastItemBeforeOrEqual(value: TDomain): TItem | undefined { - if (this._hasLastValue && CompareResult.isLessThan(this._domainComparator(value, this._lastValue!))) { - // Values must be monotonously increasing - throw new BugIndicatingError(); - } - this._lastValue = value; - this._hasLastValue = true; - - while ( - this._currentIdx < this._items.length - && CompareResult.isLessThanOrEqual(this._domainComparator( - this._itemToDomain(this._items[this._currentIdx]), - value - )) - ) { - this._currentIdx++; - } - - return this._currentIdx === 0 ? undefined : this._items[this._currentIdx - 1]; - } -} - -function intersectRanges(ranges1: LineRange[], ranges2: LineRange[]): LineRange[] { - const result: LineRange[] = []; - - let i1 = 0; - let i2 = 0; - while (i1 < ranges1.length && i2 < ranges2.length) { - const r1 = ranges1[i1]; - const r2 = ranges2[i2]; - - const i = r1.intersect(r2); - if (i && !i.isEmpty) { - result.push(i); - } - - if (r1.endLineNumberExclusive < r2.endLineNumberExclusive) { - i1++; - } else { - i2++; - } - } - - return result; -} - -// TODO make this fast -class LineRangeSet { - private readonly _normalizedRanges: LineRange[] = []; - - addRange(range: LineRange): void { - // Idea: Find joinRange such that: - // replaceRange = _normalizedRanges.replaceRange(joinRange, range.joinAll(joinRange.map(idx => this._normalizedRanges[idx]))) - - // idx of first element that touches range or that is after range - const joinRangeStartIdx = mapMinusOne(this._normalizedRanges.findIndex(r => r.endLineNumberExclusive >= range.startLineNumber), this._normalizedRanges.length); - // idx of element after { last element that touches range or that is before range } - const joinRangeEndIdxExclusive = findLastIndex(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; - - if (joinRangeStartIdx === joinRangeEndIdxExclusive) { - // If there is no element that touches range, then joinRangeStartIdx === joinRangeEndIdxExclusive and that value is the index of the element after range - this._normalizedRanges.splice(joinRangeStartIdx, 0, range); - } else if (joinRangeStartIdx === joinRangeEndIdxExclusive - 1) { - // Else, there is an element that touches range and in this case it is both the first and last element. Thus we can replace it - const joinRange = this._normalizedRanges[joinRangeStartIdx]; - this._normalizedRanges[joinRangeStartIdx] = joinRange.join(range); - } else { - // First and last element are different - we need to replace the entire range - const joinRange = this._normalizedRanges[joinRangeStartIdx].join(this._normalizedRanges[joinRangeEndIdxExclusive - 1]).join(range); - this._normalizedRanges.splice(joinRangeStartIdx, joinRangeEndIdxExclusive - joinRangeStartIdx, joinRange); - } - } - - intersects(range: LineRange): boolean { - for (const r of this._normalizedRanges) { - if (r.intersectsStrict(range)) { - return true; - } - } - return false; - } - - /** - * Subtracts all ranges in this set from `range` and returns the result. - */ - subtractFrom(range: LineRange): LineRange[] { - // idx of first element that touches range or that is after range - const joinRangeStartIdx = mapMinusOne(this._normalizedRanges.findIndex(r => r.endLineNumberExclusive >= range.startLineNumber), this._normalizedRanges.length); - // idx of element after { last element that touches range or that is before range } - const joinRangeEndIdxExclusive = findLastIndex(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; - - if (joinRangeStartIdx === joinRangeEndIdxExclusive) { - return [range]; - } - - const result: LineRange[] = []; - let startLineNumber = range.startLineNumber; - for (let i = joinRangeStartIdx; i < joinRangeEndIdxExclusive; i++) { - const r = this._normalizedRanges[i]; - if (r.startLineNumber > startLineNumber) { - result.push(new LineRange(startLineNumber, r.startLineNumber)); - } - startLineNumber = r.endLineNumberExclusive; - } - if (startLineNumber < range.endLineNumberExclusive) { - result.push(new LineRange(startLineNumber, range.endLineNumberExclusive)); - } - - return result; - } -} - -function mapMinusOne(idx: number, mapTo: number): number { - return idx === -1 ? mapTo : idx; -} - function coverFullWords(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { const additional: SequenceDiff[] = []; @@ -623,42 +509,42 @@ function mergeSequenceDiffs(sequenceDiffs1: SequenceDiff[], sequenceDiffs2: Sequ return result; } -export function lineRangeMappingFromRangeMappings(alignments: RangeMapping[], originalLines: string[], modifiedLines: string[], dontAssertStartLine: boolean = false): LineRangeMapping[] { - const changes: LineRangeMapping[] = []; - for (const g of group( +export function lineRangeMappingFromRangeMappings(alignments: RangeMapping[], originalLines: string[], modifiedLines: string[], dontAssertStartLine: boolean = false): DetailedLineRangeMapping[] { + const changes: DetailedLineRangeMapping[] = []; + for (const g of groupAdjacentBy( alignments.map(a => getLineRangeMapping(a, originalLines, modifiedLines)), (a1, a2) => - a1.originalRange.overlapOrTouch(a2.originalRange) - || a1.modifiedRange.overlapOrTouch(a2.modifiedRange) + a1.original.overlapOrTouch(a2.original) + || a1.modified.overlapOrTouch(a2.modified) )) { const first = g[0]; const last = g[g.length - 1]; - changes.push(new LineRangeMapping( - first.originalRange.join(last.originalRange), - first.modifiedRange.join(last.modifiedRange), + changes.push(new DetailedLineRangeMapping( + first.original.join(last.original), + first.modified.join(last.modified), g.map(a => a.innerChanges![0]), )); } assertFn(() => { if (!dontAssertStartLine) { - if (changes.length > 0 && changes[0].originalRange.startLineNumber !== changes[0].modifiedRange.startLineNumber) { + if (changes.length > 0 && changes[0].original.startLineNumber !== changes[0].modified.startLineNumber) { return false; } } return checkAdjacentItems(changes, - (m1, m2) => m2.originalRange.startLineNumber - m1.originalRange.endLineNumberExclusive === m2.modifiedRange.startLineNumber - m1.modifiedRange.endLineNumberExclusive && + (m1, m2) => m2.original.startLineNumber - m1.original.endLineNumberExclusive === m2.modified.startLineNumber - m1.modified.endLineNumberExclusive && // There has to be an unchanged line in between (otherwise both diffs should have been joined) - m1.originalRange.endLineNumberExclusive < m2.originalRange.startLineNumber && - m1.modifiedRange.endLineNumberExclusive < m2.modifiedRange.startLineNumber, + m1.original.endLineNumberExclusive < m2.original.startLineNumber && + m1.modified.endLineNumberExclusive < m2.modified.startLineNumber, ); }); return changes; } -export function getLineRangeMapping(rangeMapping: RangeMapping, originalLines: string[], modifiedLines: string[]): LineRangeMapping { +export function getLineRangeMapping(rangeMapping: RangeMapping, originalLines: string[], modifiedLines: string[]): DetailedLineRangeMapping { let lineStartDelta = 0; let lineEndDelta = 0; @@ -692,26 +578,7 @@ export function getLineRangeMapping(rangeMapping: RangeMapping, originalLines: s rangeMapping.modifiedRange.endLineNumber + 1 + lineEndDelta ); - return new LineRangeMapping(originalLineRange, modifiedLineRange, [rangeMapping]); -} - -function* group(items: Iterable, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable { - let currentGroup: T[] | undefined; - let last: T | undefined; - for (const item of items) { - if (last !== undefined && shouldBeGrouped(last, item)) { - currentGroup!.push(item); - } else { - if (currentGroup) { - yield currentGroup; - } - currentGroup = [item]; - } - last = item; - } - if (currentGroup) { - yield currentGroup; - } + return new DetailedLineRangeMapping(originalLineRange, modifiedLineRange, [rangeMapping]); } export class LineSequence implements ISequence { @@ -753,7 +620,7 @@ function getIndentation(str: string): number { export class LinesSliceCharSequence implements ISequence { private readonly elements: number[] = []; - private readonly firstCharOffsetByLineMinusOne: number[] = []; + private readonly firstCharOffsetByLine: number[] = []; public readonly lineRange: OffsetRange; // To account for trimming private readonly additionalOffsetByLine: number[] = []; @@ -771,6 +638,7 @@ export class LinesSliceCharSequence implements ISequence { this.lineRange = lineRange; + this.firstCharOffsetByLine[0] = 0; for (let i = this.lineRange.start; i < this.lineRange.endExclusive; i++) { let line = lines[i]; let offset = 0; @@ -793,7 +661,7 @@ export class LinesSliceCharSequence implements ISequence { // Don't add an \n that does not exist in the document. if (i < lines.length - 1) { this.elements.push('\n'.charCodeAt(0)); - this.firstCharOffsetByLineMinusOne[i - this.lineRange.start] = this.elements.length; + this.firstCharOffsetByLine[i - this.lineRange.start + 1] = this.elements.length; } } // To account for the last line @@ -852,19 +720,8 @@ export class LinesSliceCharSequence implements ISequence { return new Position(this.lineRange.start + 1, 1); } - let i = 0; - let j = this.firstCharOffsetByLineMinusOne.length; - while (i < j) { - const k = Math.floor((i + j) / 2); - if (this.firstCharOffsetByLineMinusOne[k] > offset) { - j = k; - } else { - i = k + 1; - } - } - - const offsetOfFirstCharInLine = i === 0 ? 0 : this.firstCharOffsetByLineMinusOne[i - 1]; - return new Position(this.lineRange.start + i + 1, offset - offsetOfFirstCharInLine + 1 + this.additionalOffsetByLine[i]); + const i = findLastIdxMonotonous(this.firstCharOffsetByLine, (value) => value <= offset); + return new Position(this.lineRange.start + i + 1, offset - this.firstCharOffsetByLine[i] + this.additionalOffsetByLine[i] + 1); } public translateRange(range: OffsetRange): Range { @@ -907,60 +764,12 @@ export class LinesSliceCharSequence implements ISequence { } public extendToFullLines(range: OffsetRange): OffsetRange { - const start = findLastMonotonous(this.firstCharOffsetByLineMinusOne, x => x <= range.start) ?? 0; - const end = findFirstMonotonous(this.firstCharOffsetByLineMinusOne, x => range.endExclusive <= x) ?? this.elements.length; + const start = findLastMonotonous(this.firstCharOffsetByLine, x => x <= range.start) ?? 0; + const end = findFirstMonotonous(this.firstCharOffsetByLine, x => range.endExclusive <= x) ?? this.elements.length; return new OffsetRange(start, end); } } -/** - * `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! - * - * @returns -1 if predicate is false for all items - */ -function findLastIdxMonotonous(arr: T[], predicate: (item: T) => boolean): number { - let i = 0; - let j = arr.length; - while (i < j) { - const k = Math.floor((i + j) / 2); - if (predicate(arr[k])) { - i = k + 1; - } else { - j = k; - } - } - return i - 1; -} - -export function findLastMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { - const idx = findLastIdxMonotonous(arr, predicate); - return idx === -1 ? undefined : arr[idx]; -} - -/** - * `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`! - * - * @returns arr.length if predicate is false for all items - */ -function findFirstIdxMonotonous(arr: T[], predicate: (item: T) => boolean): number { - let i = 0; - let j = arr.length; - while (i < j) { - const k = Math.floor((i + j) / 2); - if (predicate(arr[k])) { - j = k; - } else { - i = k + 1; - } - } - return i; -} - -export function findFirstMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { - const idx = findFirstIdxMonotonous(arr, predicate); - return idx === arr.length ? undefined : arr[idx]; -} - function isWordChar(charCode: number): boolean { return charCode >= CharCode.a && charCode <= CharCode.z || charCode >= CharCode.A && charCode <= CharCode.Z @@ -1033,7 +842,7 @@ class LineRangeFragment { constructor( public readonly range: LineRange, public readonly lines: string[], - public readonly source: LineRangeMapping, + public readonly source: DetailedLineRangeMapping, ) { let counter = 0; for (let i = range.startLineNumber - 1; i < range.endLineNumberExclusive - 1; i++) { diff --git a/src/vs/editor/common/diff/documentDiffProvider.ts b/src/vs/editor/common/diff/documentDiffProvider.ts index 02907dddca3..44accf9e604 100644 --- a/src/vs/editor/common/diff/documentDiffProvider.ts +++ b/src/vs/editor/common/diff/documentDiffProvider.ts @@ -5,7 +5,8 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; -import { LineRangeMapping, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from './rangeMapping'; import { ITextModel } from 'vs/editor/common/model'; /** @@ -61,7 +62,7 @@ export interface IDocumentDiff { /** * Maps all modified line ranges in the original to the corresponding line ranges in the modified text model. */ - readonly changes: readonly LineRangeMapping[]; + readonly changes: readonly DetailedLineRangeMapping[]; /** * Sorted by original line ranges. diff --git a/src/vs/editor/common/diff/legacyLinesDiffComputer.ts b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts index 5ea6524a41e..8d7e05e0308 100644 --- a/src/vs/editor/common/diff/legacyLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts @@ -5,7 +5,8 @@ import { CharCode } from 'vs/base/common/charCode'; import { IDiffChange, ISequence, LcsDiff, IDiffResult } from 'vs/base/common/diff/diff'; -import { ILinesDiffComputer, ILinesDiffComputerOptions, RangeMapping, LineRangeMapping, LinesDiff } from 'vs/editor/common/diff/linesDiffComputer'; +import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff } from 'vs/editor/common/diff/linesDiffComputer'; +import { RangeMapping, DetailedLineRangeMapping } from './rangeMapping'; import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; @@ -23,8 +24,8 @@ export class LegacyLinesDiffComputer implements ILinesDiffComputer { shouldPostProcessCharChanges: true, }); const result = diffComputer.computeDiff(); - const changes: LineRangeMapping[] = []; - let lastChange: LineRangeMapping | null = null; + const changes: DetailedLineRangeMapping[] = []; + let lastChange: DetailedLineRangeMapping | null = null; for (const c of result.changes) { @@ -44,17 +45,17 @@ export class LegacyLinesDiffComputer implements ILinesDiffComputer { modifiedRange = new LineRange(c.modifiedStartLineNumber, c.modifiedEndLineNumber + 1); } - let change = new LineRangeMapping(originalRange, modifiedRange, c.charChanges?.map(c => new RangeMapping( + let change = new DetailedLineRangeMapping(originalRange, modifiedRange, c.charChanges?.map(c => new RangeMapping( new Range(c.originalStartLineNumber, c.originalStartColumn, c.originalEndLineNumber, c.originalEndColumn), new Range(c.modifiedStartLineNumber, c.modifiedStartColumn, c.modifiedEndLineNumber, c.modifiedEndColumn), ))); if (lastChange) { - if (lastChange.modifiedRange.endLineNumberExclusive === change.modifiedRange.startLineNumber - || lastChange.originalRange.endLineNumberExclusive === change.originalRange.startLineNumber) { + if (lastChange.modified.endLineNumberExclusive === change.modified.startLineNumber + || lastChange.original.endLineNumberExclusive === change.original.startLineNumber) { // join touching diffs. Probably moving diffs up/down in the algorithm causes touching diffs. - change = new LineRangeMapping( - lastChange.originalRange.join(change.originalRange), - lastChange.modifiedRange.join(change.modifiedRange), + change = new DetailedLineRangeMapping( + lastChange.original.join(change.original), + lastChange.modified.join(change.modified), lastChange.innerChanges && change.innerChanges ? lastChange.innerChanges.concat(change.innerChanges) : undefined ); @@ -68,10 +69,10 @@ export class LegacyLinesDiffComputer implements ILinesDiffComputer { assertFn(() => { return checkAdjacentItems(changes, - (m1, m2) => m2.originalRange.startLineNumber - m1.originalRange.endLineNumberExclusive === m2.modifiedRange.startLineNumber - m1.modifiedRange.endLineNumberExclusive && + (m1, m2) => m2.original.startLineNumber - m1.original.endLineNumberExclusive === m2.modified.startLineNumber - m1.modified.endLineNumberExclusive && // There has to be an unchanged line in between (otherwise both diffs should have been joined) - m1.originalRange.endLineNumberExclusive < m2.originalRange.startLineNumber && - m1.modifiedRange.endLineNumberExclusive < m2.modifiedRange.startLineNumber, + m1.original.endLineNumberExclusive < m2.original.startLineNumber && + m1.modified.endLineNumberExclusive < m2.modified.startLineNumber, ); }); @@ -92,7 +93,7 @@ export interface IDiffComputationResult { /** * The changes as (modern) line range mapping array. */ - changes2: readonly LineRangeMapping[]; + changes2: readonly DetailedLineRangeMapping[]; } /** diff --git a/src/vs/editor/common/diff/linesDiffComputer.ts b/src/vs/editor/common/diff/linesDiffComputer.ts index d10888cb93f..a11674f0127 100644 --- a/src/vs/editor/common/diff/linesDiffComputer.ts +++ b/src/vs/editor/common/diff/linesDiffComputer.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LineRange } from 'vs/editor/common/core/lineRange'; -import { Range } from 'vs/editor/common/core/range'; +import { DetailedLineRangeMapping, LineRangeMapping } from './rangeMapping'; export interface ILinesDiffComputer { computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): LinesDiff; @@ -18,7 +17,7 @@ export interface ILinesDiffComputerOptions { export class LinesDiff { constructor( - readonly changes: readonly LineRangeMapping[], + readonly changes: readonly DetailedLineRangeMapping[], /** * Sorted by original line ranges. @@ -35,148 +34,19 @@ export class LinesDiff { } } -/** - * Maps a line range in the original text model to a line range in the modified text model. - */ -export class LineRangeMapping { - public static inverse(mapping: readonly LineRangeMapping[], originalLineCount: number, modifiedLineCount: number): LineRangeMapping[] { - const result: LineRangeMapping[] = []; - let lastOriginalEndLineNumber = 1; - let lastModifiedEndLineNumber = 1; - - for (const m of mapping) { - const r = new LineRangeMapping( - new LineRange(lastOriginalEndLineNumber, m.originalRange.startLineNumber), - new LineRange(lastModifiedEndLineNumber, m.modifiedRange.startLineNumber), - undefined - ); - if (!r.modifiedRange.isEmpty) { - result.push(r); - } - lastOriginalEndLineNumber = m.originalRange.endLineNumberExclusive; - lastModifiedEndLineNumber = m.modifiedRange.endLineNumberExclusive; - } - const r = new LineRangeMapping( - new LineRange(lastOriginalEndLineNumber, originalLineCount + 1), - new LineRange(lastModifiedEndLineNumber, modifiedLineCount + 1), - undefined - ); - if (!r.modifiedRange.isEmpty) { - result.push(r); - } - return result; - } - - /** - * The line range in the original text model. - */ - public readonly originalRange: LineRange; - - /** - * The line range in the modified text model. - */ - public readonly modifiedRange: LineRange; - - /** - * If inner changes have not been computed, this is set to undefined. - * Otherwise, it represents the character-level diff in this line range. - * The original range of each range mapping should be contained in the original line range (same for modified), exceptions are new-lines. - * Must not be an empty array. - */ - public readonly innerChanges: RangeMapping[] | undefined; - - constructor( - originalRange: LineRange, - modifiedRange: LineRange, - innerChanges: RangeMapping[] | undefined, - ) { - this.originalRange = originalRange; - this.modifiedRange = modifiedRange; - this.innerChanges = innerChanges; - } - - public toString(): string { - return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`; - } - - public get changedLineCount() { - return Math.max(this.originalRange.length, this.modifiedRange.length); - } - - public flip(): LineRangeMapping { - return new LineRangeMapping(this.modifiedRange, this.originalRange, this.innerChanges?.map(c => c.flip())); - } -} - -/** - * Maps a range in the original text model to a range in the modified text model. - */ -export class RangeMapping { - /** - * The original range. - */ - readonly originalRange: Range; - - /** - * The modified range. - */ - readonly modifiedRange: Range; - - constructor( - originalRange: Range, - - modifiedRange: Range, - ) { - this.originalRange = originalRange; - this.modifiedRange = modifiedRange; - } - - public toString(): string { - return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`; - } - - public flip(): RangeMapping { - return new RangeMapping(this.modifiedRange, this.originalRange); - } -} - -// TODO@hediet: Make LineRangeMapping extend from this! -export class SimpleLineRangeMapping { - constructor( - public readonly original: LineRange, - public readonly modified: LineRange, - ) { - } - - public toString(): string { - return `{${this.original.toString()}->${this.modified.toString()}}`; - } - - public flip(): SimpleLineRangeMapping { - return new SimpleLineRangeMapping(this.modified, this.original); - } - - public join(other: SimpleLineRangeMapping): SimpleLineRangeMapping { - return new SimpleLineRangeMapping( - this.original.join(other.original), - this.modified.join(other.modified), - ); - } -} - export class MovedText { - public readonly lineRangeMapping: SimpleLineRangeMapping; + public readonly lineRangeMapping: LineRangeMapping; /** * The diff from the original text to the moved text. * Must be contained in the original/modified line range. * Can be empty if the text didn't change (only moved). */ - public readonly changes: readonly LineRangeMapping[]; + public readonly changes: readonly DetailedLineRangeMapping[]; constructor( - lineRangeMapping: SimpleLineRangeMapping, - changes: readonly LineRangeMapping[], + lineRangeMapping: LineRangeMapping, + changes: readonly DetailedLineRangeMapping[], ) { this.lineRangeMapping = lineRangeMapping; this.changes = changes; diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts new file mode 100644 index 00000000000..12ac2a362e9 --- /dev/null +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -0,0 +1,133 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { Range } from 'vs/editor/common/core/range'; + +export class LineRangeMapping { + public static inverse(mapping: readonly DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number): DetailedLineRangeMapping[] { + const result: DetailedLineRangeMapping[] = []; + let lastOriginalEndLineNumber = 1; + let lastModifiedEndLineNumber = 1; + + for (const m of mapping) { + const r = new DetailedLineRangeMapping( + new LineRange(lastOriginalEndLineNumber, m.original.startLineNumber), + new LineRange(lastModifiedEndLineNumber, m.modified.startLineNumber), + undefined + ); + if (!r.modified.isEmpty) { + result.push(r); + } + lastOriginalEndLineNumber = m.original.endLineNumberExclusive; + lastModifiedEndLineNumber = m.modified.endLineNumberExclusive; + } + const r = new DetailedLineRangeMapping( + new LineRange(lastOriginalEndLineNumber, originalLineCount + 1), + new LineRange(lastModifiedEndLineNumber, modifiedLineCount + 1), + undefined + ); + if (!r.modified.isEmpty) { + result.push(r); + } + return result; + } + + /** + * The line range in the original text model. + */ + public readonly original: LineRange; + + /** + * The line range in the modified text model. + */ + public readonly modified: LineRange; + + constructor( + originalRange: LineRange, + modifiedRange: LineRange + ) { + this.original = originalRange; + this.modified = modifiedRange; + } + + + public toString(): string { + return `{${this.original.toString()}->${this.modified.toString()}}`; + } + + public flip(): LineRangeMapping { + return new LineRangeMapping(this.modified, this.original); + } + + public join(other: LineRangeMapping): LineRangeMapping { + return new LineRangeMapping( + this.original.join(other.original), + this.modified.join(other.modified) + ); + } + + public get changedLineCount() { + return Math.max(this.original.length, this.modified.length); + } +} + +/** + * Maps a line range in the original text model to a line range in the modified text model. + */ +export class DetailedLineRangeMapping extends LineRangeMapping { + /** + * If inner changes have not been computed, this is set to undefined. + * Otherwise, it represents the character-level diff in this line range. + * The original range of each range mapping should be contained in the original line range (same for modified), exceptions are new-lines. + * Must not be an empty array. + */ + public readonly innerChanges: RangeMapping[] | undefined; + + constructor( + originalRange: LineRange, + modifiedRange: LineRange, + innerChanges: RangeMapping[] | undefined + ) { + super(originalRange, modifiedRange); + this.innerChanges = innerChanges; + } + + public override flip(): DetailedLineRangeMapping { + return new DetailedLineRangeMapping(this.modified, this.original, this.innerChanges?.map(c => c.flip())); + } +} + +/** + * Maps a range in the original text model to a range in the modified text model. + */ +export class RangeMapping { + /** + * The original range. + */ + readonly originalRange: Range; + + /** + * The modified range. + */ + readonly modifiedRange: Range; + + constructor( + originalRange: Range, + + modifiedRange: Range + ) { + this.originalRange = originalRange; + this.modifiedRange = modifiedRange; + } + + public toString(): string { + return `{${this.originalRange.toString()}->${this.modifiedRange.toString()}}`; + } + + public flip(): RangeMapping { + return new RangeMapping(this.modifiedRange, this.originalRange); + } +} diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index ca8364f62d3..335e6b5c883 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -21,7 +21,8 @@ import { IEditorWorkerHost } from 'vs/editor/common/services/editorWorkerHost'; import { StopWatch } from 'vs/base/common/stopwatch'; import { UnicodeTextModelHighlighter, UnicodeHighlighterOptions } from 'vs/editor/common/services/unicodeTextModelHighlighter'; import { DiffComputer, IChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; -import { ILinesDiffComputer, ILinesDiffComputerOptions, LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { ILinesDiffComputer, ILinesDiffComputerOptions } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from '../diff/rangeMapping'; import { linesDiffComputers } from 'vs/editor/common/diff/linesDiffComputers'; import { createProxyObject, getAllMethodNames } from 'vs/base/common/objects'; import { IDocumentDiffProviderOptions } from 'vs/editor/common/diff/documentDiffProvider'; @@ -422,8 +423,8 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { const identical = (result.changes.length > 0 ? false : this._modelsAreIdentical(originalTextModel, modifiedTextModel)); - function getLineChanges(changes: readonly LineRangeMapping[]): ILineChange[] { - return changes.map(m => ([m.originalRange.startLineNumber, m.originalRange.endLineNumberExclusive, m.modifiedRange.startLineNumber, m.modifiedRange.endLineNumberExclusive, m.innerChanges?.map(m => [ + function getLineChanges(changes: readonly DetailedLineRangeMapping[]): ILineChange[] { + return changes.map(m => ([m.original.startLineNumber, m.original.endLineNumberExclusive, m.modified.startLineNumber, m.modified.endLineNumberExclusive, m.innerChanges?.map(m => [ m.originalRange.startLineNumber, m.originalRange.startColumn, m.originalRange.endLineNumber, diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 005e48ad530..bf010adb6ab 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -34,7 +34,8 @@ import { EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensi import { IMenuItem, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; -import { LineRangeMapping, MovedText, RangeMapping, SimpleLineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping, RangeMapping, LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -584,11 +585,11 @@ export function createMonacoEditorAPI(): typeof monaco.editor { FindMatch: FindMatch, ApplyUpdateResult: ApplyUpdateResult, LineRange: LineRange, - LineRangeMapping: LineRangeMapping, + LineRangeMapping: DetailedLineRangeMapping, RangeMapping: RangeMapping, EditorZoom: EditorZoom, MovedText: MovedText, - SimpleLineRangeMapping: SimpleLineRangeMapping, + SimpleLineRangeMapping: LineRangeMapping, // vars EditorType: EditorType, diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts index c9980e34a08..6adb654d142 100644 --- a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts +++ b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts @@ -6,7 +6,7 @@ import assert = require('assert'); import { UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; import { LineRange } from 'vs/editor/common/core/lineRange'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; suite('DiffEditorWidget2', () => { suite('UnchangedRegion', () => { @@ -16,7 +16,7 @@ suite('DiffEditorWidget2', () => { test('Everything changed', () => { assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( - [new LineRangeMapping(new LineRange(1, 10), new LineRange(1, 10), [])], + [new DetailedLineRangeMapping(new LineRange(1, 10), new LineRange(1, 10), [])], 10, 10, 3, @@ -38,7 +38,7 @@ suite('DiffEditorWidget2', () => { test('Change in the middle', () => { assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( - [new LineRangeMapping(new LineRange(50, 60), new LineRange(50, 60), [])], + [new DetailedLineRangeMapping(new LineRange(50, 60), new LineRange(50, 60), [])], 100, 100, 3, @@ -51,7 +51,7 @@ suite('DiffEditorWidget2', () => { test('Change at the end', () => { assert.deepStrictEqual(serialize(UnchangedRegion.fromDiffs( - [new LineRangeMapping(new LineRange(99, 100), new LineRange(100, 100), [])], + [new DetailedLineRangeMapping(new LineRange(99, 100), new LineRange(100, 100), [])], 100, 100, 3, diff --git a/src/vs/editor/test/common/core/lineRange.test.ts b/src/vs/editor/test/common/core/lineRange.test.ts new file mode 100644 index 00000000000..08aa57bae8d --- /dev/null +++ b/src/vs/editor/test/common/core/lineRange.test.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert = require('assert'); +import { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; + +suite('LineRange', () => { + test('contains', () => { + const r = new LineRange(2, 3); + assert.deepStrictEqual(r.contains(1), false); + assert.deepStrictEqual(r.contains(2), true); + assert.deepStrictEqual(r.contains(3), true); + assert.deepStrictEqual(r.contains(4), false); + }); +}); + +suite('LineRangeSet', () => { + test('addRange', () => { + const set = new LineRangeSet(); + set.addRange(new LineRange(2, 3)); + set.addRange(new LineRange(3, 4)); + set.addRange(new LineRange(10, 20)); + assert.deepStrictEqual(set.toString(), '[2,4), [10,20)'); + + set.addRange(new LineRange(3, 21)); + assert.deepStrictEqual(set.toString(), '[2,21)'); + }); + + test('getUnion', () => { + const set1 = new LineRangeSet([ + new LineRange(2, 3), + new LineRange(5, 7), + new LineRange(10, 20) + ]); + const set2 = new LineRangeSet([ + new LineRange(3, 4), + new LineRange(6, 8), + new LineRange(9, 11) + ]); + + const union = set1.getUnion(set2); + assert.deepStrictEqual(union.toString(), '[2,4), [5,8), [9,20)'); + }); + + test('intersects', () => { + const set1 = new LineRangeSet([ + new LineRange(2, 3), + new LineRange(5, 7), + new LineRange(10, 20) + ]); + + assert.deepStrictEqual(set1.intersects(new LineRange(1, 2)), false); + assert.deepStrictEqual(set1.intersects(new LineRange(1, 3)), true); + assert.deepStrictEqual(set1.intersects(new LineRange(3, 5)), false); + }); +}); diff --git a/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts new file mode 100644 index 00000000000..f5e4bbacdbc --- /dev/null +++ b/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.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 * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { LinesSliceCharSequence, getLineRangeMapping } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; + +suite('lineRangeMapping', () => { + test('1', () => { + assert.deepStrictEqual( + getLineRangeMapping( + new RangeMapping( + new Range(2, 1, 3, 1), + new Range(2, 1, 2, 1) + ), + [ + 'const abc = "helloworld".split("");', + '', + '' + ], + [ + 'const asciiLower = "helloworld".split("");', + '' + ] + ).toString(), + "{[2,3)->[2,2)}" + ); + }); + + test('2', () => { + assert.deepStrictEqual( + getLineRangeMapping( + new RangeMapping( + new Range(2, 1, 2, 1), + new Range(2, 1, 4, 1), + ), + [ + '', + '', + ], + [ + '', + '', + '', + '', + ] + ).toString(), + "{[2,2)->[2,4)}" + ); + }); +}); + +suite('LinesSliceCharSequence', () => { + // Create tests for translateOffset + + const sequence = new LinesSliceCharSequence( + [ + 'line1: foo', + 'line2: fizzbuzz', + 'line3: barr', + 'line4: hello world', + 'line5: bazz', + ], + new OffsetRange(1, 4), true + ); + + test('translateOffset', () => { + assert.deepStrictEqual( + { result: OffsetRange.ofLength(sequence.length).map(offset => sequence.translateOffset(offset).toString()) }, + ({ + result: [ + "(2,1)", "(2,2)", "(2,3)", "(2,4)", "(2,5)", "(2,6)", "(2,7)", "(2,8)", "(2,9)", "(2,10)", "(2,11)", + "(2,12)", "(2,13)", "(2,14)", "(2,15)", "(2,16)", + + "(3,1)", "(3,2)", "(3,3)", "(3,4)", "(3,5)", "(3,6)", "(3,7)", "(3,8)", "(3,9)", "(3,10)", "(3,11)", "(3,12)", + + "(4,1)", "(4,2)", "(4,3)", "(4,4)", "(4,5)", "(4,6)", "(4,7)", "(4,8)", "(4,9)", + "(4,10)", "(4,11)", "(4,12)", "(4,13)", "(4,14)", "(4,15)", "(4,16)", "(4,17)", + "(4,18)", "(4,19)" + ] + }) + ); + }); + + test('extendToFullLines', () => { + assert.deepStrictEqual( + { result: sequence.getText(sequence.extendToFullLines(new OffsetRange(20, 25))) }, + ({ result: "line3: barr\n" }) + ); + + assert.deepStrictEqual( + { result: sequence.getText(sequence.extendToFullLines(new OffsetRange(20, 45))) }, + ({ result: "line3: barr\nline4: hello world\n" }) + ); + }); +}); diff --git a/src/vs/editor/test/node/diffing/diffingFixture.test.ts b/src/vs/editor/test/node/diffing/diffingFixture.test.ts index a04f2edf9d8..89490813b78 100644 --- a/src/vs/editor/test/node/diffing/diffingFixture.test.ts +++ b/src/vs/editor/test/node/diffing/diffingFixture.test.ts @@ -8,7 +8,7 @@ import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs import { join, resolve } from 'path'; import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { FileAccess } from 'vs/base/common/network'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; @@ -43,10 +43,10 @@ suite('diff fixtures', () => { const ignoreTrimWhitespace = folder.indexOf('trimws') >= 0; const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }); - function getDiffs(changes: readonly LineRangeMapping[]): IDetailedDiff[] { + function getDiffs(changes: readonly DetailedLineRangeMapping[]): IDetailedDiff[] { return changes.map(c => ({ - originalRange: c.originalRange.toString(), - modifiedRange: c.modifiedRange.toString(), + originalRange: c.original.toString(), + modifiedRange: c.modified.toString(), innerChanges: c.innerChanges?.map(c => ({ originalRange: c.originalRange.toString(), modifiedRange: c.modifiedRange.toString(), diff --git a/src/vs/editor/test/node/diffing/lineRangeMapping.test.ts b/src/vs/editor/test/node/diffing/lineRangeMapping.test.ts deleted file mode 100644 index f0d0802912d..00000000000 --- a/src/vs/editor/test/node/diffing/lineRangeMapping.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { Range } from 'vs/editor/common/core/range'; -import { RangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; -import { getLineRangeMapping } from 'vs/editor/common/diff/advancedLinesDiffComputer'; - -suite('lineRangeMapping', () => { - test('1', () => { - assert.deepStrictEqual( - getLineRangeMapping( - new RangeMapping( - new Range(2, 1, 3, 1), - new Range(2, 1, 2, 1) - ), - [ - 'const abc = "helloworld".split("");', - '', - '' - ], - [ - 'const asciiLower = "helloworld".split("");', - '' - ] - ).toString(), - "{[2,3)->[2,2)}" - ); - }); - - test('2', () => { - assert.deepStrictEqual( - getLineRangeMapping( - new RangeMapping( - new Range(2, 1, 2, 1), - new Range(2, 1, 4, 1), - ), - [ - '', - '', - ], - [ - '', - '', - '', - '', - ] - ).toString(), - "{[2,2)->[2,4)}" - ); - }); -}); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index 1374da20090..a9b339e7ae5 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -18,7 +18,7 @@ import * as editorColorRegistry from 'vs/editor/common/core/editorColorRegistry' import { IThemeService } from 'vs/platform/theme/common/themeService'; import { INLINE_CHAT_ID, inlineChatDiffInserted, inlineChatDiffRemoved, inlineChatRegionHighlight } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { LineRange } from 'vs/editor/common/core/lineRange'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { Position } from 'vs/editor/common/core/position'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { ScrollType } from 'vs/editor/common/editorCommon'; @@ -163,7 +163,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { this._isDiffLocked = true; } - private _updateFromChanges(range: Range, changes: readonly LineRangeMapping[]): void { + private _updateFromChanges(range: Range, changes: readonly DetailedLineRangeMapping[]): void { assertType(this.editor.hasModel()); if (this._isDiffLocked) { @@ -177,7 +177,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { // --- full diff - private _renderChangesWithFullDiff(changes: readonly LineRangeMapping[], range: Range) { + private _renderChangesWithFullDiff(changes: readonly DetailedLineRangeMapping[], range: Range) { const modified = this.editor.getModel()!; const ranges = this._computeHiddenRanges(modified, range, changes); @@ -206,16 +206,16 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { super.hide(); } - private _computeHiddenRanges(model: ITextModel, range: Range, changes: readonly LineRangeMapping[]) { + private _computeHiddenRanges(model: ITextModel, range: Range, changes: readonly DetailedLineRangeMapping[]) { if (changes.length === 0) { - changes = [new LineRangeMapping(LineRange.fromRange(range), LineRange.fromRange(range), undefined)]; + changes = [new DetailedLineRangeMapping(LineRange.fromRange(range), LineRange.fromRange(range), undefined)]; } - let originalLineRange = changes[0].originalRange; - let modifiedLineRange = changes[0].modifiedRange; + let originalLineRange = changes[0].original; + let modifiedLineRange = changes[0].modified; for (let i = 1; i < changes.length; i++) { - originalLineRange = originalLineRange.join(changes[i].originalRange); - modifiedLineRange = modifiedLineRange.join(changes[i].modifiedRange); + originalLineRange = originalLineRange.join(changes[i].original); + modifiedLineRange = modifiedLineRange.join(changes[i].modified); } const startDelta = modifiedLineRange.startLineNumber - range.startLineNumber; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index fcbb998dc79..8de79727cea 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -23,7 +23,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Iterable } from 'vs/base/common/iterator'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { isCancellationError } from 'vs/base/common/errors'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { raceCancellation } from 'vs/base/common/async'; @@ -112,7 +112,7 @@ export class Session { private _lastInput: SessionPrompt | undefined; private _lastExpansionState: ExpansionState | undefined; - private _lastTextModelChanges: readonly LineRangeMapping[] | undefined; + private _lastTextModelChanges: readonly DetailedLineRangeMapping[] | undefined; private _isUnstashed: boolean = false; private readonly _exchange: SessionExchange[] = []; private readonly _startTime = new Date(); @@ -191,7 +191,7 @@ export class Session { return this._lastTextModelChanges ?? []; } - set lastTextModelChanges(changes: readonly LineRangeMapping[]) { + set lastTextModelChanges(changes: readonly DetailedLineRangeMapping[]) { this._lastTextModelChanges = changes; } @@ -207,8 +207,8 @@ export class Session { let startLine = Number.MAX_VALUE; let endLine = Number.MIN_VALUE; for (const change of this._lastTextModelChanges) { - startLine = Math.min(startLine, change.modifiedRange.startLineNumber); - endLine = Math.max(endLine, change.modifiedRange.endLineNumberExclusive); + startLine = Math.min(startLine, change.modified.startLineNumber); + endLine = Math.max(endLine, change.modified.endLineNumberExclusive); } return this.textModelN.getValueInRange(new Range(startLine, 1, endLine, Number.MAX_VALUE)); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index c3a08e55ce9..76944f5b38e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -354,7 +354,7 @@ export class LiveStrategy extends EditModeStrategy { const lastTextModelChanges = this._session.lastTextModelChanges; let lastLineOfLocalEdits: number | undefined; for (const change of lastTextModelChanges) { - const changeEndLineNumber = change.modifiedRange.endLineNumberExclusive - 1; + const changeEndLineNumber = change.modified.endLineNumberExclusive - 1; if (typeof lastLineOfLocalEdits === 'undefined' || lastLineOfLocalEdits < changeEndLineNumber) { lastLineOfLocalEdits = changeEndLineNumber; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 343749ce5c6..9703af6a00a 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -36,7 +36,7 @@ import { FileKind } from 'vs/platform/files/common/files'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { LanguageSelector } from 'vs/editor/common/languageSelector'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; -import { LineRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { invertLineRange, lineRangeAsRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ICodeEditorViewState, ScrollType } from 'vs/editor/common/editorCommon'; import { LineRange } from 'vs/editor/common/core/lineRange'; @@ -612,7 +612,7 @@ export class InlineChatWidget { // --- preview - showEditsPreview(textModelv0: ITextModel, allEdits: ISingleEditOperation[][], changes: readonly LineRangeMapping[]) { + showEditsPreview(textModelv0: ITextModel, allEdits: ISingleEditOperation[][], changes: readonly DetailedLineRangeMapping[]) { if (changes.length === 0) { this.hideEditsPreview(); return; @@ -628,11 +628,11 @@ export class InlineChatWidget { this._previewDiffEditor.value.setModel({ original: textModelv0, modified }); // joined ranges - let originalLineRange = changes[0].originalRange; - let modifiedLineRange = changes[0].modifiedRange; + let originalLineRange = changes[0].original; + let modifiedLineRange = changes[0].modified; for (let i = 1; i < changes.length; i++) { - originalLineRange = originalLineRange.join(changes[i].originalRange); - modifiedLineRange = modifiedLineRange.join(changes[i].modifiedRange); + originalLineRange = originalLineRange.join(changes[i].original); + modifiedLineRange = modifiedLineRange.join(changes[i].modified); } // apply extra padding diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts index cc1be27460f..334479df903 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts @@ -5,7 +5,7 @@ import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; import { IReader } from 'vs/base/common/observable'; -import { RangeMapping as DiffRangeMapping } from 'vs/editor/common/diff/linesDiffComputer'; +import { RangeMapping as DiffRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -56,9 +56,9 @@ export class MergeDiffComputer implements IMergeDiffComputer { const changes = result.changes.map(c => new DetailedLineRangeMapping( - toLineRange(c.originalRange), + toLineRange(c.original), textModel1, - toLineRange(c.modifiedRange), + toLineRange(c.modified), textModel2, c.innerChanges?.map(ic => toRangeMapping(ic)) ) diff --git a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts index d411e9a4c1f..0441e4483d3 100644 --- a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts +++ b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts @@ -290,9 +290,9 @@ class MergeModelInterface extends Disposable { ); const changes = result.changes.map(c => new DetailedLineRangeMapping( - toLineRange(c.originalRange), + toLineRange(c.original), textModel1, - toLineRange(c.modifiedRange), + toLineRange(c.modified), textModel2, c.innerChanges?.map(ic => toRangeMapping(ic)).filter(isDefined) ) From 4d53e0a13649b3a49e4ee2f2d15577aa581b70bb Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 1 Sep 2023 13:32:21 +0200 Subject: [PATCH 434/607] Fixes CI --- build/monaco/monaco.d.ts.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 89c884f18a0..3af07387f51 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -110,7 +110,7 @@ export interface ICommandHandler { #include(vs/editor/common/diff/legacyLinesDiffComputer): IChange, ICharChange, ILineChange #include(vs/editor/common/diff/documentDiffProvider): IDocumentDiffProvider, IDocumentDiffProviderOptions, IDocumentDiff #include(vs/editor/common/core/lineRange): LineRange -#include(vs/editor/common/diff/linesDiffComputer): LineRangeMapping, RangeMapping, MovedText, SimpleLineRangeMapping +#include(vs/editor/common/diff/linesDiffComputer): DetailedLineRangeMapping, RangeMapping, MovedText, LineRangeMapping #include(vs/editor/common/core/dimension): IDimension #includeAll(vs/editor/common/editorCommon): IScrollEvent #includeAll(vs/editor/common/textModelEvents): From fe25a72de8ea3cefcf95946bb9f3c82bef001b6d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 1 Sep 2023 14:01:32 +0200 Subject: [PATCH 435/607] Fixes CI --- build/monaco/monaco.d.ts.recipe | 3 +- .../standalone/browser/standaloneEditor.ts | 4 +- src/vs/monaco.d.ts | 58 ++++++++----------- 3 files changed, 29 insertions(+), 36 deletions(-) diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 3af07387f51..4f064aeb6f1 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -110,7 +110,8 @@ export interface ICommandHandler { #include(vs/editor/common/diff/legacyLinesDiffComputer): IChange, ICharChange, ILineChange #include(vs/editor/common/diff/documentDiffProvider): IDocumentDiffProvider, IDocumentDiffProviderOptions, IDocumentDiff #include(vs/editor/common/core/lineRange): LineRange -#include(vs/editor/common/diff/linesDiffComputer): DetailedLineRangeMapping, RangeMapping, MovedText, LineRangeMapping +#include(vs/editor/common/diff/linesDiffComputer): MovedText +#include(vs/editor/common/diff/rangeMapping): DetailedLineRangeMapping, RangeMapping, LineRangeMapping #include(vs/editor/common/core/dimension): IDimension #includeAll(vs/editor/common/editorCommon): IScrollEvent #includeAll(vs/editor/common/textModelEvents): diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index bf010adb6ab..9d529b71049 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -585,11 +585,11 @@ export function createMonacoEditorAPI(): typeof monaco.editor { FindMatch: FindMatch, ApplyUpdateResult: ApplyUpdateResult, LineRange: LineRange, - LineRangeMapping: DetailedLineRangeMapping, + DetailedLineRangeMapping: DetailedLineRangeMapping, RangeMapping: RangeMapping, EditorZoom: EditorZoom, MovedText: MovedText, - SimpleLineRangeMapping: LineRangeMapping, + LineRangeMapping: LineRangeMapping, // vars EditorType: EditorType, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 3a49dbf4aa5..51ce58011f8 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2415,7 +2415,7 @@ declare namespace monaco.editor { /** * Maps all modified line ranges in the original to the corresponding line ranges in the modified text model. */ - readonly changes: readonly LineRangeMapping[]; + readonly changes: readonly DetailedLineRangeMapping[]; /** * Sorted by original line ranges. * The original line ranges and the modified line ranges must be disjoint (but can be touching). @@ -2433,11 +2433,6 @@ declare namespace monaco.editor { * @param lineRanges An array of sorted line ranges. */ static joinMany(lineRanges: readonly (readonly LineRange[])[]): readonly LineRange[]; - /** - * @param lineRanges1 Must be sorted. - * @param lineRanges2 Must be sorted. - */ - static join(lineRanges1: readonly LineRange[], lineRanges2: readonly LineRange[]): readonly LineRange[]; static ofLength(startLineNumber: number, length: number): LineRange; /** * The start line number. @@ -2485,19 +2480,22 @@ declare namespace monaco.editor { includes(lineNumber: number): boolean; } + export class MovedText { + readonly lineRangeMapping: LineRangeMapping; + /** + * The diff from the original text to the moved text. + * Must be contained in the original/modified line range. + * Can be empty if the text didn't change (only moved). + */ + readonly changes: readonly DetailedLineRangeMapping[]; + constructor(lineRangeMapping: LineRangeMapping, changes: readonly DetailedLineRangeMapping[]); + flip(): MovedText; + } + /** * Maps a line range in the original text model to a line range in the modified text model. */ - export class LineRangeMapping { - static inverse(mapping: readonly LineRangeMapping[], originalLineCount: number, modifiedLineCount: number): LineRangeMapping[]; - /** - * The line range in the original text model. - */ - readonly originalRange: LineRange; - /** - * The line range in the modified text model. - */ - readonly modifiedRange: LineRange; + export class DetailedLineRangeMapping extends LineRangeMapping { /** * If inner changes have not been computed, this is set to undefined. * Otherwise, it represents the character-level diff in this line range. @@ -2506,9 +2504,7 @@ declare namespace monaco.editor { */ readonly innerChanges: RangeMapping[] | undefined; constructor(originalRange: LineRange, modifiedRange: LineRange, innerChanges: RangeMapping[] | undefined); - toString(): string; - get changedLineCount(): any; - flip(): LineRangeMapping; + flip(): DetailedLineRangeMapping; } /** @@ -2528,25 +2524,21 @@ declare namespace monaco.editor { flip(): RangeMapping; } - export class MovedText { - readonly lineRangeMapping: SimpleLineRangeMapping; + export class LineRangeMapping { + static inverse(mapping: readonly DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number): DetailedLineRangeMapping[]; /** - * The diff from the original text to the moved text. - * Must be contained in the original/modified line range. - * Can be empty if the text didn't change (only moved). + * The line range in the original text model. */ - readonly changes: readonly LineRangeMapping[]; - constructor(lineRangeMapping: SimpleLineRangeMapping, changes: readonly LineRangeMapping[]); - flip(): MovedText; - } - - export class SimpleLineRangeMapping { readonly original: LineRange; + /** + * The line range in the modified text model. + */ readonly modified: LineRange; - constructor(original: LineRange, modified: LineRange); + constructor(originalRange: LineRange, modifiedRange: LineRange); toString(): string; - flip(): SimpleLineRangeMapping; - join(other: SimpleLineRangeMapping): SimpleLineRangeMapping; + flip(): LineRangeMapping; + join(other: LineRangeMapping): LineRangeMapping; + get changedLineCount(): any; } export interface IDimension { width: number; From 0ae7b5b1c5aef9c195c1b6366b7a4e19fdecb314 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 1 Sep 2023 14:21:24 +0200 Subject: [PATCH 436/607] Fixes tests --- src/vs/editor/test/common/core/lineRange.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/test/common/core/lineRange.test.ts b/src/vs/editor/test/common/core/lineRange.test.ts index 08aa57bae8d..535a20607b1 100644 --- a/src/vs/editor/test/common/core/lineRange.test.ts +++ b/src/vs/editor/test/common/core/lineRange.test.ts @@ -11,7 +11,7 @@ suite('LineRange', () => { const r = new LineRange(2, 3); assert.deepStrictEqual(r.contains(1), false); assert.deepStrictEqual(r.contains(2), true); - assert.deepStrictEqual(r.contains(3), true); + assert.deepStrictEqual(r.contains(3), false); assert.deepStrictEqual(r.contains(4), false); }); }); From 660e12b312542b6fc0f6fe5d2fe7c5b749a19af1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Sep 2023 15:32:38 +0200 Subject: [PATCH 437/607] editors - restore focus also in `addGroup` (#191961) --- src/vs/workbench/browser/parts/editor/editorPart.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index c424f439a4f..8b733caa867 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -515,12 +515,21 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroupView { const locationView = this.assertGroupView(location); + const restoreFocus = this.shouldRestoreFocus(locationView.element); + const group = this.doAddGroup(locationView, direction); if (options?.activate) { this.doSetGroupActive(group); } + // Restore focus if we had it previously after completing the grid + // operation. That operation might cause reparenting of grid views + // which moves focus to the element otherwise. + if (restoreFocus) { + locationView.focus(); + } + return group; } From 4f424db46e05e241a7f3440384bd82b7ea393b17 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Sep 2023 15:33:06 +0200 Subject: [PATCH 438/607] debt - ensure `file` scheme when using `.fsPath` (#191960) --- src/vs/code/electron-main/main.ts | 8 ++++---- src/vs/code/node/cliProcessMain.ts | 2 +- .../node/sharedProcess/contrib/logsDataCleaner.ts | 11 ++++++----- .../node/sharedProcess/contrib/storageDataCleaner.ts | 6 ++++-- .../protocol/electron-main/protocolMainService.ts | 4 ++-- src/vs/platform/storage/common/storageService.ts | 7 ++++--- src/vs/platform/storage/electron-main/storageMain.ts | 7 ++++--- .../storage/electron-main/storageMainService.ts | 3 ++- .../terminal/electron-main/electronPtyHostStarter.ts | 3 ++- src/vs/platform/terminal/node/nodePtyHostStarter.ts | 4 ++-- .../windows/electron-main/windowsMainService.ts | 6 +++--- .../electron-main/workspacesManagementMainService.ts | 4 ++-- src/vs/server/node/serverServices.ts | 2 +- .../contrib/files/electron-sandbox/fileCommands.ts | 2 +- .../electron-sandbox/localHistoryCommands.ts | 2 +- .../contrib/logs/electron-sandbox/logsActions.ts | 5 +++-- .../electron-sandbox/userDataSync.contribution.ts | 3 ++- 17 files changed, 44 insertions(+), 35 deletions(-) diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 0174a24c3c8..7bdb1e1a459 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -251,10 +251,10 @@ class CodeMain { Promise.all([ environmentMainService.extensionsPath, environmentMainService.codeCachePath, - environmentMainService.logsHome.fsPath, - userDataProfilesMainService.defaultProfile.globalStorageHome.fsPath, - environmentMainService.workspaceStorageHome.fsPath, - environmentMainService.localHistoryHome.fsPath, + environmentMainService.logsHome.with({ scheme: Schemas.file }).fsPath, + userDataProfilesMainService.defaultProfile.globalStorageHome.with({ scheme: Schemas.file }).fsPath, + environmentMainService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath, + environmentMainService.localHistoryHome.with({ scheme: Schemas.file }).fsPath, environmentMainService.backupHome ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index a003267c043..b97aa7a0be8 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -121,7 +121,7 @@ class CliMain extends Disposable { // Init folders await Promise.all([ - environmentService.appSettingsHome.fsPath, + environmentService.appSettingsHome.with({ scheme: Schemas.file }).fsPath, environmentService.extensionsPath ].map(path => path ? Promises.mkdir(path, { recursive: true }) : undefined)); diff --git a/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts b/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts index ab5f8a1d87a..60a3652edf0 100644 --- a/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts +++ b/src/vs/code/node/sharedProcess/contrib/logsDataCleaner.ts @@ -6,7 +6,9 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; -import { basename, dirname, joinPath } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; +import { join } from 'vs/base/common/path'; +import { basename, dirname } from 'vs/base/common/resources'; import { Promises } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; @@ -30,9 +32,8 @@ export class LogsDataCleaner extends Disposable { try { const currentLog = basename(this.environmentService.logsHome); - const logsRoot = dirname(this.environmentService.logsHome); - - const logFiles = await Promises.readdir(logsRoot.fsPath); + const logsRoot = dirname(this.environmentService.logsHome.with({ scheme: Schemas.file })).fsPath; + const logFiles = await Promises.readdir(logsRoot); const allSessions = logFiles.filter(logFile => /^\d{8}T\d{6}$/.test(logFile)); const oldSessions = allSessions.sort().filter(session => session !== currentLog); @@ -41,7 +42,7 @@ export class LogsDataCleaner extends Disposable { if (sessionsToDelete.length > 0) { this.logService.trace(`[logs cleanup]: Removing log folders '${sessionsToDelete.join(', ')}'`); - await Promise.all(sessionsToDelete.map(sessionToDelete => Promises.rm(joinPath(logsRoot, sessionToDelete).fsPath))); + await Promise.all(sessionsToDelete.map(sessionToDelete => Promises.rm(join(logsRoot, sessionToDelete)))); } } catch (error) { onUnexpectedError(error); diff --git a/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts index fc3f2eb117a..da67be66109 100644 --- a/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts @@ -15,6 +15,7 @@ import { EXTENSION_DEVELOPMENT_EMPTY_WINDOW_WORKSPACE } from 'vs/platform/worksp import { NON_EMPTY_WORKSPACE_ID_LENGTH } from 'vs/platform/workspaces/node/workspaces'; import { INativeHostService } from 'vs/platform/native/common/native'; import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService'; +import { Schemas } from 'vs/base/common/network'; export class UnusedWorkspaceStorageDataCleaner extends Disposable { @@ -36,11 +37,12 @@ export class UnusedWorkspaceStorageDataCleaner extends Disposable { this.logService.trace('[storage cleanup]: Starting to clean up workspace storage folders for unused empty workspaces.'); try { - const workspaceStorageFolders = await Promises.readdir(this.environmentService.workspaceStorageHome.fsPath); + const workspaceStorageHome = this.environmentService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath; + const workspaceStorageFolders = await Promises.readdir(workspaceStorageHome); const storageClient = new StorageClient(this.mainProcessService.getChannel('storage')); await Promise.all(workspaceStorageFolders.map(async workspaceStorageFolder => { - const workspaceStoragePath = join(this.environmentService.workspaceStorageHome.fsPath, workspaceStorageFolder); + const workspaceStoragePath = join(workspaceStorageHome, workspaceStorageFolder); if (workspaceStorageFolder.length === NON_EMPTY_WORKSPACE_ID_LENGTH) { return; // keep workspace storage for folders/workspaces that can be accessed still diff --git a/src/vs/platform/protocol/electron-main/protocolMainService.ts b/src/vs/platform/protocol/electron-main/protocolMainService.ts index 79d431b275c..2b0a52627a8 100644 --- a/src/vs/platform/protocol/electron-main/protocolMainService.ts +++ b/src/vs/platform/protocol/electron-main/protocolMainService.ts @@ -39,8 +39,8 @@ 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(userDataProfilesService.defaultProfile.globalStorageHome.fsPath); - this.addValidFileRoot(environmentService.workspaceStorageHome.fsPath); + this.addValidFileRoot(userDataProfilesService.defaultProfile.globalStorageHome.with({ scheme: Schemas.file }).fsPath); + this.addValidFileRoot(environmentService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath); // Handle protocols this.handleProtocols(); diff --git a/src/vs/platform/storage/common/storageService.ts b/src/vs/platform/storage/common/storageService.ts index d5231239e0c..4cfe09ec6d4 100644 --- a/src/vs/platform/storage/common/storageService.ts +++ b/src/vs/platform/storage/common/storageService.ts @@ -5,6 +5,7 @@ import { Promises } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Schemas } from 'vs/base/common/network'; import { joinPath } from 'vs/base/common/resources'; import { IStorage, Storage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -117,11 +118,11 @@ export class RemoteStorageService extends AbstractStorageService { protected getLogDetails(scope: StorageScope): string | undefined { switch (scope) { case StorageScope.APPLICATION: - return this.applicationStorageProfile.globalStorageHome.fsPath; + return this.applicationStorageProfile.globalStorageHome.with({ scheme: Schemas.file }).fsPath; case StorageScope.PROFILE: - return this.profileStorageProfile?.globalStorageHome.fsPath; + return this.profileStorageProfile?.globalStorageHome.with({ scheme: Schemas.file }).fsPath; default: - return this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; + return this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').with({ scheme: Schemas.file }).fsPath}` : undefined; } } diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 6acc9643b91..b993f4cb3cd 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -20,6 +20,7 @@ import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; +import { Schemas } from 'vs/base/common/network'; export interface IStorageMainOptions { @@ -275,7 +276,7 @@ class BaseProfileAwareStorageMain extends BaseStorageMain { get path(): string | undefined { if (!this.options.useInMemoryStorage) { - return join(this.profile.globalStorageHome.fsPath, BaseProfileAwareStorageMain.STORAGE_NAME); + return join(this.profile.globalStorageHome.with({ scheme: Schemas.file }).fsPath, BaseProfileAwareStorageMain.STORAGE_NAME); } return undefined; @@ -352,7 +353,7 @@ export class WorkspaceStorageMain extends BaseStorageMain { get path(): string | undefined { if (!this.options.useInMemoryStorage) { - return join(this.environmentService.workspaceStorageHome.fsPath, this.workspace.id, WorkspaceStorageMain.WORKSPACE_STORAGE_NAME); + return join(this.environmentService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath, this.workspace.id, WorkspaceStorageMain.WORKSPACE_STORAGE_NAME); } return undefined; @@ -384,7 +385,7 @@ export class WorkspaceStorageMain extends BaseStorageMain { } // Otherwise, ensure the storage folder exists on disk - const workspaceStorageFolderPath = join(this.environmentService.workspaceStorageHome.fsPath, this.workspace.id); + const workspaceStorageFolderPath = join(this.environmentService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath, this.workspace.id); const workspaceStorageDatabasePath = join(workspaceStorageFolderPath, WorkspaceStorageMain.WORKSPACE_STORAGE_NAME); const storageExists = await Promises.exists(workspaceStorageFolderPath); diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index dc8b3c51e59..bdfad4eacb6 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -19,6 +19,7 @@ import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userData import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { Schemas } from 'vs/base/common/network'; //#region Storage Main Service (intent: make application, profile and workspace storage accessible to windows from main process) @@ -359,7 +360,7 @@ export class ApplicationStorageMainService extends AbstractStorageService implem protected getLogDetails(scope: StorageScope): string | undefined { if (scope === StorageScope.APPLICATION) { - return this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath; + return this.userDataProfilesService.defaultProfile.globalStorageHome.with({ scheme: Schemas.file }).fsPath; } return undefined; // any other scope is unsupported from main process diff --git a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts index a1599f50737..8c74c72b9c9 100644 --- a/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts +++ b/src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts @@ -18,6 +18,7 @@ import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecy import { Emitter } from 'vs/base/common/event'; import { deepClone } from 'vs/base/common/objects'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { Schemas } from 'vs/base/common/network'; export class ElectronPtyHostStarter extends Disposable implements IPtyHostStarter { @@ -58,7 +59,7 @@ export class ElectronPtyHostStarter extends Disposable implements IPtyHostStarte type: 'ptyHost', entryPoint: 'vs/platform/terminal/node/ptyHostMain', execArgv, - args: ['--logsPath', this._environmentMainService.logsHome.fsPath], + args: ['--logsPath', this._environmentMainService.logsHome.with({ scheme: Schemas.file }).fsPath], env: this._createPtyHostConfiguration() }); diff --git a/src/vs/platform/terminal/node/nodePtyHostStarter.ts b/src/vs/platform/terminal/node/nodePtyHostStarter.ts index a1d4e8b7d88..d5a1a43724a 100644 --- a/src/vs/platform/terminal/node/nodePtyHostStarter.ts +++ b/src/vs/platform/terminal/node/nodePtyHostStarter.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { FileAccess } from 'vs/base/common/network'; +import { FileAccess, Schemas } from 'vs/base/common/network'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentService'; @@ -22,7 +22,7 @@ export class NodePtyHostStarter extends Disposable implements IPtyHostStarter { start(): IPtyHostConnection { const opts: IIPCOptions = { serverName: 'Pty Host', - args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath], + args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.with({ scheme: Schemas.file }).fsPath], env: { VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', VSCODE_PIPE_LOGGING: 'true', diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 022a20e4ee0..67d7113b000 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1401,8 +1401,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic profile: defaultProfile }, - homeDir: this.environmentMainService.userHome.fsPath, - tmpDir: this.environmentMainService.tmpDir.fsPath, + homeDir: this.environmentMainService.userHome.with({ scheme: Schemas.file }).fsPath, + tmpDir: this.environmentMainService.tmpDir.with({ scheme: Schemas.file }).fsPath, userDataDir: this.environmentMainService.userDataPath, remoteAuthority: options.remoteAuthority, @@ -1419,7 +1419,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic window: [], global: this.loggerService.getRegisteredLoggers() }, - logsPath: this.environmentMainService.logsHome.fsPath, + logsPath: this.environmentMainService.logsHome.with({ scheme: Schemas.file }).fsPath, product, isInitialStartup: options.initialStartup, diff --git a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts index 34913c5ffc5..1d482c3eba4 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts @@ -84,7 +84,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork // Resolve untitled workspaces try { - const untitledWorkspacePaths = (await Promises.readdir(this.untitledWorkspacesHome.fsPath)).map(folder => joinPath(this.untitledWorkspacesHome, folder, UNTITLED_WORKSPACE_NAME)); + const untitledWorkspacePaths = (await Promises.readdir(this.untitledWorkspacesHome.with({ scheme: Schemas.file }).fsPath)).map(folder => joinPath(this.untitledWorkspacesHome, folder, UNTITLED_WORKSPACE_NAME));// for (const untitledWorkspacePath of untitledWorkspacePaths) { const workspace = getWorkspaceIdentifier(untitledWorkspacePath); const resolvedWorkspace = await this.resolveLocalWorkspace(untitledWorkspacePath); @@ -227,7 +227,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork await Promises.rm(dirname(configPath)); // Mark Workspace Storage to be deleted - const workspaceStoragePath = join(this.environmentMainService.workspaceStorageHome.fsPath, workspace.id); + const workspaceStoragePath = join(this.environmentMainService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath, workspace.id); if (await Promises.exists(workspaceStoragePath)) { await Promises.writeFile(join(workspaceStoragePath, 'obsolete'), ''); } diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index d291af5dd1c..7e568185997 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -100,7 +100,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken const logger = loggerService.createLogger('remoteagent', { name: localize('remoteExtensionLog', "Server") }); const logService = new LogService(logger, [new ServerLogger(getLogLevel(environmentService))]); services.set(ILogService, logService); - setTimeout(() => cleanupOlderLogs(environmentService.logsHome.fsPath).then(null, err => logService.error(err)), 10000); + setTimeout(() => cleanupOlderLogs(environmentService.logsHome.with({ scheme: Schemas.file }).fsPath).then(null, err => logService.error(err)), 10000); logService.onDidChangeLogLevel(logLevel => log(logService, logLevel, `Log level changed to ${LogLevelToString(logService.getLevel())}`)); logService.trace(`Remote configuration data at ${REMOTE_DATA_FOLDER}`); diff --git a/src/vs/workbench/contrib/files/electron-sandbox/fileCommands.ts b/src/vs/workbench/contrib/files/electron-sandbox/fileCommands.ts index eb3744a25a3..4f62c9c804c 100644 --- a/src/vs/workbench/contrib/files/electron-sandbox/fileCommands.ts +++ b/src/vs/workbench/contrib/files/electron-sandbox/fileCommands.ts @@ -15,7 +15,7 @@ export function revealResourcesInOS(resources: URI[], nativeHostService: INative if (resources.length) { sequence(resources.map(r => async () => { if (r.scheme === Schemas.file || r.scheme === Schemas.vscodeUserData) { - nativeHostService.showItemInFolder(r.fsPath); + nativeHostService.showItemInFolder(r.with({ scheme: Schemas.file }).fsPath); } })); } else if (workspaceContextService.getWorkspace().folders.length) { diff --git a/src/vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands.ts b/src/vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands.ts index 9ac4e2cd92f..20d20356e6e 100644 --- a/src/vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands.ts +++ b/src/vs/workbench/contrib/localHistory/electron-sandbox/localHistoryCommands.ts @@ -39,7 +39,7 @@ registerAction2(class extends Action2 { const { entry } = await findLocalHistoryEntry(workingCopyHistoryService, item); if (entry) { - await nativeHostService.showItemInFolder(entry.location.fsPath); + await nativeHostService.showItemInFolder(entry.location.with({ scheme: Schemas.file }).fsPath); } } }); diff --git a/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts b/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts index 3a057d2b8b9..cbc2a01dbb5 100644 --- a/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts +++ b/src/vs/workbench/contrib/logs/electron-sandbox/logsActions.ts @@ -9,6 +9,7 @@ import { INativeHostService } from 'vs/platform/native/common/native'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; import { joinPath } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; export class OpenLogsFolderAction extends Action { @@ -23,7 +24,7 @@ export class OpenLogsFolderAction extends Action { } override run(): Promise { - return this.nativeHostService.showItemInFolder(joinPath(this.environmentService.logsHome, 'main.log').fsPath); + return this.nativeHostService.showItemInFolder(joinPath(this.environmentService.logsHome, 'main.log').with({ scheme: Schemas.file }).fsPath); } } @@ -43,7 +44,7 @@ export class OpenExtensionLogsFolderAction extends Action { override async run(): Promise { const folderStat = await this.fileService.resolve(this.environmentSerice.extHostLogsPath); if (folderStat.children && folderStat.children[0]) { - return this.nativeHostService.showItemInFolder(folderStat.children[0].resource.fsPath); + return this.nativeHostService.showItemInFolder(folderStat.children[0].resource.with({ scheme: Schemas.file }).fsPath); } } } diff --git a/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts index 644e1ad6dec..abbf3cfc882 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-sandbox/userDataSync.contribution.ts @@ -17,6 +17,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { INativeHostService } from 'vs/platform/native/common/native'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { CONTEXT_SYNC_STATE, SYNC_TITLE } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { Schemas } from 'vs/base/common/network'; class UserDataSyncServicesContribution implements IWorkbenchContribution { @@ -51,7 +52,7 @@ registerAction2(class OpenSyncBackupsFolder extends Action2 { if (await fileService.exists(syncHome)) { const folderStat = await fileService.resolve(syncHome); const item = folderStat.children && folderStat.children[0] ? folderStat.children[0].resource : syncHome; - return nativeHostService.showItemInFolder(item.fsPath); + return nativeHostService.showItemInFolder(item.with({ scheme: Schemas.file }).fsPath); } else { notificationService.info(localize('no backups', "Local backups folder does not exist")); } From 7545ee2ec46897d2b7ce3c6d591f636962455c9a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Sep 2023 16:31:57 +0200 Subject: [PATCH 439/607] Creating a new empty editor group can leave focus in inactive group (fix #189256) (#191996) --- .../api/browser/mainThreadEditorTabs.ts | 2 +- .../workbench/browser/parts/editor/editor.ts | 4 +- .../browser/parts/editor/editorActions.ts | 24 +++++++-- .../browser/parts/editor/editorPart.ts | 50 +++++++++---------- .../browser/gettingStarted.ts | 3 +- .../editor/common/editorGroupsService.ts | 7 +-- .../test/browser/editorGroupsService.test.ts | 3 +- .../test/browser/workbenchTestServices.ts | 6 +-- 8 files changed, 54 insertions(+), 45 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts index f1d2d01597f..3da6c5ed51d 100644 --- a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts +++ b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts @@ -575,7 +575,7 @@ export class MainThreadEditorTabs implements MainThreadEditorTabsShape { if (viewColumn === SIDE_GROUP) { direction = preferredSideBySideGroupDirection(this._configurationService); } - targetGroup = this._editorGroupsService.addGroup(this._editorGroupsService.groups[this._editorGroupsService.groups.length - 1], direction, undefined); + targetGroup = this._editorGroupsService.addGroup(this._editorGroupsService.groups[this._editorGroupsService.groups.length - 1], direction); } else { targetGroup = this._editorGroupsService.getGroup(groupId); } diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 68f64a44162..23f3371411a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -5,7 +5,7 @@ import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorIdentifier, IEditorCloseEvent, IEditorPartOptions, IEditorPartOptionsChangeEvent, SideBySideEditor, EditorCloseContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup, GroupDirection, IMergeGroupOptions, GroupsOrder, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Dimension } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; @@ -96,7 +96,7 @@ export interface IEditorGroupsAccessor { activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView; restoreGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView; - addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroupView; + addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView; mergeGroup(group: IEditorGroupView | GroupIdentifier, target: IEditorGroupView | GroupIdentifier, options?: IMergeGroupOptions): IEditorGroupView; moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView; diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index e13b41e522f..bcb01677992 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -2263,13 +2263,27 @@ abstract class AbstractCreateEditorGroupAction extends Action2 { override async run(accessor: ServicesAccessor): Promise { const editorGroupService = accessor.get(IEditorGroupsService); + const layoutService = accessor.get(IWorkbenchLayoutService); - // We intentionally do not want the new group to be focussed so that - // a user can have keyboard focus e.g. in a tree/list, open a new - // editor group that is active and then arrow-up/down in the tree/list - // to pick an editor to open in that group + // We are about to create a new empty editor group. We make an opiniated + // decision here whether to focus that new editor group or not based + // on what is currently focused. If focus is outside the editor area not + // in the , we do not focus, with the rationale that a user might + // have focus on a tree/list with the intention to pick an element to + // open in the new group from that tree/list. + // + // If focus is inside the editor area, we want to prevent the situation + // of an editor having keyboard focus in an inactive editor group + // (see https://github.com/microsoft/vscode/issues/189256) - editorGroupService.addGroup(editorGroupService.activeGroup, this.direction, { activate: true }); + const focusNewGroup = layoutService.hasFocus(Parts.EDITOR_PART) || document.activeElement === document.body; + + const group = editorGroupService.addGroup(editorGroupService.activeGroup, this.direction); + editorGroupService.activateGroup(group); + + if (focusNewGroup) { + group.focus(); + } } } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 8b733caa867..af10ae9c109 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -8,7 +8,7 @@ import { Part } from 'vs/workbench/browser/part'; import { Dimension, isAncestor, $, EventHelper, addDisposableGenericMouseDownListener } from 'vs/base/browser/dom'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, GroupsOrder, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService, IEditorSideGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { GroupDirection, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, GroupsOrder, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService, IEditorSideGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IView, orthogonal, LayoutPriority, IViewSize, Direction, SerializableGrid, Sizing, ISerializedGrid, ISerializedNode, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid'; import { GroupIdentifier, EditorInputWithOptions, IEditorPartOptions, IEditorPartOptionsChangeEvent, GroupModelChangeKind } from 'vs/workbench/common/editor'; @@ -328,7 +328,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro const groupView = this.assertGroupView(group); this.doSetGroupActive(groupView); - this._onDidActivateGroup.fire(groupView); return groupView; } @@ -512,17 +511,13 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro return false; } - addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroupView { + addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView { const locationView = this.assertGroupView(location); const restoreFocus = this.shouldRestoreFocus(locationView.element); const group = this.doAddGroup(locationView, direction); - if (options?.activate) { - this.doSetGroupActive(group); - } - // Restore focus if we had it previously after completing the grid // operation. That operation might cause reparenting of grid views // which moves focus to the element otherwise. @@ -622,27 +617,30 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro } private doSetGroupActive(group: IEditorGroupView): void { - if (this._activeGroup === group) { - return; // return if this is already the active group + if (this._activeGroup !== group) { + const previousActiveGroup = this._activeGroup; + this._activeGroup = group; + + // Update list of most recently active groups + this.doUpdateMostRecentActive(group, true); + + // Mark previous one as inactive + previousActiveGroup?.setActive(false); + + // Mark group as new active + group.setActive(true); + + // Maximize the group if it is currently minimized + this.doRestoreGroup(group); + + // Event + this._onDidChangeActiveGroup.fire(group); } - const previousActiveGroup = this._activeGroup; - this._activeGroup = group; - - // Update list of most recently active groups - this.doUpdateMostRecentActive(group, true); - - // Mark previous one as inactive - previousActiveGroup?.setActive(false); - - // Mark group as new active - group.setActive(true); - - // Maximize the group if it is currently minimized - this.doRestoreGroup(group); - - // Event - this._onDidChangeActiveGroup.fire(group); + // Always fire the event that a group has been activated + // even if its the same group that is already active to + // signal the intent even when nothing has changed. + this._onDidActivateGroup.fire(group); } private doRestoreGroup(group: IEditorGroupView): void { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 04283236ddd..d42c329a868 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -1229,7 +1229,8 @@ export class GettingStartedPage extends EditorPane { if (toSide && fullSize.width > 700) { if (this.groupsService.count === 1) { - this.groupsService.addGroup(this.groupsService.groups[0], GroupDirection.RIGHT, { activate: true }); + const sideGroup = this.groupsService.addGroup(this.groupsService.groups[0], GroupDirection.RIGHT); + this.groupsService.activateGroup(sideGroup); const gettingStartedSize = Math.floor(fullSize.width / 2); diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index e2021cc43ad..12a0b1cab6f 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -91,10 +91,6 @@ export interface EditorGroupLayout { groups: GroupLayoutArgument[]; } -export interface IAddGroupOptions { - activate?: boolean; -} - export const enum MergeGroupMode { COPY_EDITORS, MOVE_EDITORS @@ -364,9 +360,8 @@ export interface IEditorGroupsService { * * @param location the group from which to split to add a new group * @param direction the direction of where to split to - * @param options configure the newly group with options */ - addGroup(location: IEditorGroup | GroupIdentifier, direction: GroupDirection, options?: IAddGroupOptions): IEditorGroup; + addGroup(location: IEditorGroup | GroupIdentifier, direction: GroupDirection): IEditorGroup; /** * Remove a group from the editor area. diff --git a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts index 24f173d1c66..e05cee676fc 100644 --- a/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorGroupsService.test.ts @@ -320,7 +320,8 @@ suite('EditorGroupsService', () => { const input = new TestFileEditorInput(URI.file('foo/bar'), TEST_EDITOR_INPUT_ID); await rootGroup.openEditor(input, { pinned: true }); - const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT, { activate: true }); + const rightGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); + part.activateGroup(rightGroup); const downGroup = part.copyGroup(rootGroup, rightGroup, GroupDirection.DOWN); assert.strictEqual(groupAddedCounter, 2); assert.strictEqual(downGroup.count, 1); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index c566bc46401..ed87b23c2ba 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -52,7 +52,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IDecorationsService, IResourceDecorationChangeEvent, IDecoration, IDecorationData, IDecorationsProvider } from 'vs/workbench/services/decorations/common/decorations'; import { IDisposable, toDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IAddGroupOptions, IMergeGroupOptions, IEditorReplacement, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions, GroupOrientation, ICloseAllEditorsOptions, ICloseEditorsFilter } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, GroupDirection, IMergeGroupOptions, IEditorReplacement, IFindGroupScope, EditorGroupLayout, ICloseEditorOptions, GroupOrientation, ICloseAllEditorsOptions, ICloseEditorsFilter } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, ISaveEditorsOptions, IRevertAllEditorsOptions, PreferredGroup, IEditorsChangeEvent, ISaveEditorsResult } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorPaneRegistry, EditorPaneDescriptor } from 'vs/workbench/browser/editor'; @@ -848,7 +848,7 @@ export class TestEditorGroupsService implements IEditorGroupsService { applyLayout(_layout: EditorGroupLayout): void { } getLayout(): EditorGroupLayout { throw new Error('not implemented'); } setGroupOrientation(_orientation: GroupOrientation): void { } - addGroup(_location: number | IEditorGroup, _direction: GroupDirection, _options?: IAddGroupOptions): IEditorGroup { throw new Error('not implemented'); } + addGroup(_location: number | IEditorGroup, _direction: GroupDirection): IEditorGroup { throw new Error('not implemented'); } removeGroup(_group: number | IEditorGroup): void { } moveGroup(_group: number | IEditorGroup, _location: number | IEditorGroup, _direction: GroupDirection): IEditorGroup { throw new Error('not implemented'); } mergeGroup(_group: number | IEditorGroup, _target: number | IEditorGroup, _options?: IMergeGroupOptions): IEditorGroup { throw new Error('not implemented'); } @@ -946,7 +946,7 @@ export class TestEditorGroupAccessor implements IEditorGroupsAccessor { getGroups(order: GroupsOrder): IEditorGroupView[] { throw new Error('Method not implemented.'); } activateGroup(identifier: number | IEditorGroupView): IEditorGroupView { throw new Error('Method not implemented.'); } restoreGroup(identifier: number | IEditorGroupView): IEditorGroupView { throw new Error('Method not implemented.'); } - addGroup(location: number | IEditorGroupView, direction: GroupDirection, options?: IAddGroupOptions | undefined): IEditorGroupView { throw new Error('Method not implemented.'); } + addGroup(location: number | IEditorGroupView, direction: GroupDirection): IEditorGroupView { throw new Error('Method not implemented.'); } mergeGroup(group: number | IEditorGroupView, target: number | IEditorGroupView, options?: IMergeGroupOptions | undefined): IEditorGroupView { throw new Error('Method not implemented.'); } moveGroup(group: number | IEditorGroupView, location: number | IEditorGroupView, direction: GroupDirection): IEditorGroupView { throw new Error('Method not implemented.'); } copyGroup(group: number | IEditorGroupView, location: number | IEditorGroupView, direction: GroupDirection): IEditorGroupView { throw new Error('Method not implemented.'); } From 9ee5a2123dc1ef1e407c499aac1c296ba03eb1c4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Sep 2023 16:37:19 +0200 Subject: [PATCH 440/607] [Accessibility] Consider providing the default keybinding for notifications.showList (fix #191784) (#191997) --- .../parts/notifications/notificationsCommands.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 52910366b5f..6d02c45b4ca 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -6,7 +6,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { INotificationViewItem, isNotificationViewItem, NotificationsModel } from 'vs/workbench/common/notifications'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; @@ -90,9 +90,15 @@ export function getNotificationFromContext(listService: IListService, context?: export function registerNotificationCommands(center: INotificationsCenterController, toasts: INotificationsToastController, model: NotificationsModel): void { // Show Notifications Cneter - CommandsRegistry.registerCommand(SHOW_NOTIFICATIONS_CENTER, () => { - toasts.hide(); - center.show(); + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: SHOW_NOTIFICATIONS_CENTER, + weight: KeybindingWeight.WorkbenchContrib, + when: NotificationsCenterVisibleContext.negate(), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyN), + handler: () => { + toasts.hide(); + center.show(); + } }); // Hide Notifications Center From 459508aba91f11f9e39351081be92127e06615a3 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Fri, 1 Sep 2023 07:38:07 -0700 Subject: [PATCH 441/607] rm terminal tab focus mode --- .../browser/config/editorConfiguration.ts | 4 +- src/vs/editor/browser/config/tabFocus.ts | 18 ++----- .../editor/browser/widget/codeEditorWidget.ts | 6 +-- .../browser/toggleTabFocusMode.ts | 11 ++-- .../browser/parts/editor/editorStatus.ts | 9 ++-- .../browser/parts/editor/tabFocus.ts | 53 ++----------------- .../terminal/browser/terminalInstance.ts | 4 +- .../terminal/common/terminalConfiguration.ts | 5 -- 8 files changed, 23 insertions(+), 87 deletions(-) diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts index fc197da1d57..cf9154ce7d6 100644 --- a/src/vs/editor/browser/config/editorConfiguration.ts +++ b/src/vs/editor/browser/config/editorConfiguration.ts @@ -12,7 +12,7 @@ import * as platform from 'vs/base/common/platform'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { FontMeasurements } from 'vs/editor/browser/config/fontMeasurements'; import { migrateOptions } from 'vs/editor/browser/config/migrateOptions'; -import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus'; +import { TabFocus } from 'vs/editor/browser/config/tabFocus'; import { ComputeOptionsMemory, ConfigurationChangedEvent, EditorOption, editorOptionsRegistry, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, IEnvironmentalOptions } from 'vs/editor/common/config/editorOptions'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { BareFontInfo, FontInfo, IValidatedEditorOptions } from 'vs/editor/common/config/fontInfo'; @@ -117,7 +117,7 @@ export class EditorConfiguration extends Disposable implements IEditorConfigurat lineNumbersDigitCount: this._lineNumbersDigitCount, emptySelectionClipboard: partialEnv.emptySelectionClipboard, pixelRatio: partialEnv.pixelRatio, - tabFocusMode: TabFocus.getTabFocusMode(TabFocusContext.Editor), + tabFocusMode: TabFocus.getTabFocusMode(), accessibilitySupport: partialEnv.accessibilitySupport, glyphMarginDecorationLaneCount: this._glyphMarginDecorationLaneCount }; diff --git a/src/vs/editor/browser/config/tabFocus.ts b/src/vs/editor/browser/config/tabFocus.ts index 3575cfed1af..c42402aa964 100644 --- a/src/vs/editor/browser/config/tabFocus.ts +++ b/src/vs/editor/browser/config/tabFocus.ts @@ -5,28 +5,18 @@ import { Emitter, Event } from 'vs/base/common/event'; -export const enum TabFocusContext { - Terminal = 'terminalFocus', - Editor = 'editorFocus' -} - class TabFocusImpl { - private _tabFocusTerminal: boolean = false; private _tabFocusEditor: boolean = false; private readonly _onDidChangeTabFocus = new Emitter(); public readonly onDidChangeTabFocus: Event = this._onDidChangeTabFocus.event; - public getTabFocusMode(context: TabFocusContext): boolean { - return context === TabFocusContext.Terminal ? this._tabFocusTerminal : this._tabFocusEditor; + public getTabFocusMode(): boolean { + return this._tabFocusEditor; } - public setTabFocusMode(tabFocusMode: boolean, context: TabFocusContext): void { - if (context === TabFocusContext.Terminal) { - this._tabFocusTerminal = tabFocusMode; - } else { - this._tabFocusEditor = tabFocusMode; - } + public setTabFocusMode(tabFocusMode: boolean): void { + this._tabFocusEditor = tabFocusMode; this._onDidChangeTabFocus.fire(); } } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index a6f59231e39..a4c07939710 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -59,7 +59,7 @@ import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguratio import { IDimension } from 'vs/editor/common/core/dimension'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { CodeEditorContributions } from 'vs/editor/browser/widget/codeEditorContributions'; -import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus'; +import { TabFocus } from 'vs/editor/browser/config/tabFocus'; let EDITOR_ID = 0; @@ -2024,7 +2024,7 @@ class EditorContextKeysManager extends Disposable { this._register(this._editor.onDidBlurEditorText(() => this._updateFromFocus())); this._register(this._editor.onDidChangeModel(() => this._updateFromModel())); this._register(this._editor.onDidChangeConfiguration(() => this._updateFromModel())); - this._register(TabFocus.onDidChangeTabFocus(() => this._editorTabMovesFocus.set(TabFocus.getTabFocusMode(TabFocusContext.Editor)))); + this._register(TabFocus.onDidChangeTabFocus(() => this._editorTabMovesFocus.set(TabFocus.getTabFocusMode()))); this._updateFromConfig(); this._updateFromSelection(); @@ -2037,7 +2037,7 @@ class EditorContextKeysManager extends Disposable { private _updateFromConfig(): void { const options = this._editor.getOptions(); - this._editorTabMovesFocus.set(TabFocus.getTabFocusMode(TabFocusContext.Editor)); + this._editorTabMovesFocus.set(TabFocus.getTabFocusMode()); this._editorReadonly.set(options.get(EditorOption.readOnly)); this._inDiffEditor.set(options.get(EditorOption.inDiffEditor)); this._editorColumnSelection.set(options.get(EditorOption.columnSelection)); diff --git a/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts b/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts index 44e53dcee12..2dfd2309a8e 100644 --- a/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts +++ b/src/vs/editor/contrib/toggleTabFocusMode/browser/toggleTabFocusMode.ts @@ -5,11 +5,9 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus'; -import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { TabFocus } from 'vs/editor/browser/config/tabFocus'; import * as nls from 'vs/nls'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class ToggleTabFocusModeAction extends Action2 { @@ -30,11 +28,10 @@ export class ToggleTabFocusModeAction extends Action2 { }); } - public run(accessor: ServicesAccessor): void { - const context = accessor.get(IContextKeyService).getContextKeyValue('focusedView') === 'terminal' ? TabFocusContext.Terminal : TabFocusContext.Editor; - const oldValue = TabFocus.getTabFocusMode(context); + public run(): void { + const oldValue = TabFocus.getTabFocusMode(); const newValue = !oldValue; - TabFocus.setTabFocusMode(newValue, context); + TabFocus.setTabFocusMode(newValue); if (newValue) { alert(nls.localize('toggle.tabMovesFocus.on', "Pressing Tab will now move focus to the next focusable element")); } else { diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index dcb7c46f57a..40de5fbfa2a 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -28,7 +28,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILanguageService, ILanguageSelection } from 'vs/editor/common/languages/language'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus'; +import { TabFocus } from 'vs/editor/browser/config/tabFocus'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { EncodingMode, IEncodingSupport, ILanguageSupport, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -50,7 +50,7 @@ import { IMarker, IMarkerService, MarkerSeverity, IMarkerData } from 'vs/platfor import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { AutomaticLanguageDetectionLikelyWrongClassification, AutomaticLanguageDetectionLikelyWrongId, IAutomaticLanguageDetectionLikelyWrongData, ILanguageDetectionService } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Action2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -310,8 +310,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { @ILanguageService private readonly languageService: ILanguageService, @ITextFileService private readonly textFileService: ITextFileService, @IStatusbarService private readonly statusbarService: IStatusbarService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); this.tabFocusMode = instantiationService.createInstance(TabFocusMode); @@ -821,7 +820,7 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } private onTabFocusModeChange(): void { - const info: StateDelta = { type: 'tabFocusMode', tabFocusMode: TabFocus.getTabFocusMode(this.contextKeyService.getContextKeyValue('focusedView') === 'terminal' ? TabFocusContext.Terminal : TabFocusContext.Editor) }; + const info: StateDelta = { type: 'tabFocusMode', tabFocusMode: TabFocus.getTabFocusMode() }; this.updateState(info); } diff --git a/src/vs/workbench/browser/parts/editor/tabFocus.ts b/src/vs/workbench/browser/parts/editor/tabFocus.ts index 52f2e0d7199..66515874450 100644 --- a/src/vs/workbench/browser/parts/editor/tabFocus.ts +++ b/src/vs/workbench/browser/parts/editor/tabFocus.ts @@ -5,19 +5,16 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { TabFocusContext, TabFocus } from 'vs/editor/browser/config/tabFocus'; +import { TabFocus } from 'vs/editor/browser/config/tabFocus'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { TerminalSettingId, terminalTabFocusModeContextKey } from 'vs/platform/terminal/common/terminal'; export const editorTabFocusContextKey = new RawContextKey('editorTabFocusMode', false, true); export class TabFocusMode extends Disposable { - private _previousViewContext?: TabFocusContext; private readonly _onDidChange = this._register(new Emitter()); readonly onDidChange = this._onDidChange.event; private _editorContext: IContextKey; - private _terminalContext: IContextKey; constructor( @IContextKeyService contextKeyService: IContextKeyService, @@ -26,59 +23,17 @@ export class TabFocusMode extends Disposable { super(); this._editorContext = editorTabFocusContextKey.bindTo(contextKeyService); - this._terminalContext = terminalTabFocusModeContextKey.bindTo(contextKeyService); const editorConfig: boolean = configurationService.getValue('editor.tabFocusMode'); - const terminalConfig: boolean = configurationService.getValue(TerminalSettingId.TabFocusMode) ?? editorConfig; this._editorContext.set(editorConfig); - this._terminalContext.set(terminalConfig); - TabFocus.setTabFocusMode(editorConfig, TabFocusContext.Editor); - TabFocus.setTabFocusMode(terminalConfig, TabFocusContext.Terminal); - const viewKey = new Set(); - viewKey.add('focusedView'); - this._register(contextKeyService.onDidChangeContext((c) => { - if (c.affectsSome(viewKey)) { - const terminalFocus = contextKeyService.getContextKeyValue('focusedView') === 'terminal'; - const context = terminalFocus ? TabFocusContext.Terminal : TabFocusContext.Editor; - if (this._previousViewContext === context) { - return; - } - if (terminalFocus) { - this._editorContext.reset(); - } else { - this._terminalContext.reset(); - } - this._previousViewContext = context; - this._onDidChange.fire(); - } - })); + TabFocus.setTabFocusMode(editorConfig); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor.tabFocusMode')) { const editorConfig: boolean = configurationService.getValue('editor.tabFocusMode'); - TabFocus.setTabFocusMode(editorConfig, TabFocusContext.Editor); + TabFocus.setTabFocusMode(editorConfig); this._editorContext.set(editorConfig); - const terminalConfig: boolean = configurationService.getValue(TerminalSettingId.TabFocusMode); - if (terminalConfig === null) { - // editor config overrides - configurationService.updateValue(TerminalSettingId.TabFocusMode, editorConfig); - TabFocus.setTabFocusMode(editorConfig, TabFocusContext.Terminal); - this._terminalContext.set(editorConfig); - } - this._onDidChange.fire(); - } else if (e.affectsConfiguration(TerminalSettingId.TabFocusMode)) { - const terminalConfig: boolean = configurationService.getValue(TerminalSettingId.TabFocusMode) ?? configurationService.getValue('editor.tabFocusMode'); - configurationService.updateValue(TerminalSettingId.TabFocusMode, terminalConfig); - TabFocus.setTabFocusMode(terminalConfig, TabFocusContext.Terminal); - this._terminalContext.set(terminalConfig); this._onDidChange.fire(); } })); - TabFocus.onDidChangeTabFocus(() => { - const focusedView = contextKeyService.getContextKeyValue('focusedView') === 'terminal' ? TabFocusContext.Terminal : TabFocusContext.Editor; - if (focusedView === TabFocusContext.Terminal) { - this._terminalContext.set(TabFocus.getTabFocusMode(focusedView)); - } else { - this._editorContext.set(TabFocus.getTabFocusMode(focusedView)); - } - }); + TabFocus.onDidChangeTabFocus(() => this._editorContext.set(TabFocus.getTabFocusMode())); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 645bcb88540..d1365b440f2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -23,7 +23,7 @@ import * as path from 'vs/base/common/path'; import { OS, OperatingSystem, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { URI } from 'vs/base/common/uri'; -import { TabFocus, TabFocusContext } from 'vs/editor/browser/config/tabFocus'; +import { TabFocus } from 'vs/editor/browser/config/tabFocus'; import * as nls from 'vs/nls'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; @@ -972,7 +972,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } // If tab focus mode is on, tab is not passed to the terminal - if (TabFocus.getTabFocusMode(TabFocusContext.Terminal) && event.key === 'Tab') { + if (TabFocus.getTabFocusMode() && event.key === 'Tab') { return false; } diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index dde5c45fd5c..ea7c0335df5 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -106,11 +106,6 @@ const terminalConfiguration: IConfigurationNode = { default: 'right', description: localize('terminal.integrated.tabs.location', "Controls the location of the terminal tabs, either to the left or right of the actual terminal(s).") }, - [TerminalSettingId.TabFocusMode]: { - markdownDescription: localize('tabFocusMode', "Controls whether the terminal receives tabs or defers them to the workbench for navigation. When set, this overrides {0} when the terminal is focused.", '`#editor.tabFocusMode#`'), - type: ['boolean', 'null'], - default: null - }, [TerminalSettingId.DefaultLocation]: { type: 'string', enum: [TerminalLocationString.Editor, TerminalLocationString.TerminalView], From 37390a84c6bf3a7118bb9c9618e5ad402919dc28 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Sep 2023 16:40:31 +0200 Subject: [PATCH 442/607] editors - call `focus` before `activate` to preserve activation (#191991) --- src/vs/workbench/browser/parts/editor/editorPart.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index af10ae9c109..d8250c91f15 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -525,6 +525,10 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro locationView.focus(); } + if (options?.activate) { + this.doSetGroupActive(group); + } + return group; } From 14555a512349eb06555e5ae78384bd1fc46ea2e5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 1 Sep 2023 16:40:42 +0200 Subject: [PATCH 443/607] app - ensure to remove `windowId=_blank` from protocol links (fix #191902) (#191990) --- src/vs/code/electron-main/app.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index c6536298e05..49f3c2703cd 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -774,7 +774,17 @@ export class CodeApplication extends Disposable { if (secondSlash !== -1) { const authority = uri.path.substring(1, secondSlash); const path = uri.path.substring(secondSlash); - const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority, path, query: uri.query, fragment: uri.fragment }); + + let query = uri.query; + const params = new URLSearchParams(uri.query); + if (params.get('windowId') === '_blank') { + // Make sure to unset any `windowId=_blank` here + // https://github.com/microsoft/vscode/issues/191902 + params.delete('windowId'); + query = params.toString(); + } + + const remoteUri = URI.from({ scheme: Schemas.vscodeRemote, authority, path, query, fragment: uri.fragment }); if (hasWorkspaceFileExtension(path)) { return { workspaceUri: remoteUri }; From be570fd3de6ecf0935a6d8e188e4a47ae457448d Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 1 Sep 2023 16:40:56 +0200 Subject: [PATCH 444/607] Git - Bump which package (#191992) --- extensions/git/package.json | 2 +- extensions/git/yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 37212410bfe..9ad1978be0e 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3012,7 +3012,7 @@ "jschardet": "3.0.0", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", - "which": "3.0.1" + "which": "4.0.0" }, "devDependencies": { "@types/byline": "4.2.31", diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index bb3a09d947c..0b62d7472be 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -516,10 +516,10 @@ is-core-module@^2.13.0: dependencies: has "^1.0.3" -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== jschardet@3.0.0: version "3.0.0" @@ -679,12 +679,12 @@ vscode-uri@^2.0.0: resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.0.0.tgz#2df704222f72b8a71ff266ba0830ed6c51ac1542" integrity sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw== -which@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/which/-/which-3.0.1.tgz#89f1cd0c23f629a8105ffe69b8172791c87b4be1" - integrity sha512-XA1b62dzQzLfaEOSQFTCOd5KFf/1VSzZo7/7TUjnya6u0vGGKzU96UQBZTAThCb2j4/xjBAyii1OhRLJEivHvg== +which@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== dependencies: - isexe "^2.0.0" + isexe "^3.1.1" yallist@^4.0.0: version "4.0.0" From 04f02b504323d91a2eed9d827163b713a1a47859 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 1 Sep 2023 16:53:04 +0200 Subject: [PATCH 445/607] fix https://github.com/microsoft/vscode/issues/191908 --- .../codelens/browser/codelensController.ts | 33 +++++++++++++++---- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/codelens/browser/codelensController.ts b/src/vs/editor/contrib/codelens/browser/codelensController.ts index 4fd3619fb51..da810c9a099 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensController.ts +++ b/src/vs/editor/contrib/codelens/browser/codelensController.ts @@ -232,6 +232,9 @@ export class CodeLensContribution implements IEditorContribution { this._localToDispose.add(this._editor.onDidFocusEditorWidget(() => { scheduler.schedule(); })); + this._localToDispose.add(this._editor.onDidBlurEditorText(() => { + scheduler.cancel(); + })); this._localToDispose.add(this._editor.onDidScrollChange(e => { if (e.scrollTopChanged && this._lenses.length > 0) { this._resolveCodeLensesInViewportSoon(); @@ -444,8 +447,12 @@ export class CodeLensContribution implements IEditorContribution { }); } - getModel(): CodeLensModel | undefined { - return this._currentCodeLensModel; + async getModel(): Promise { + await this._getCodeLensModelPromise; + await this._resolveCodeLensesPromise; + return !this._currentCodeLensModel?.isDisposed + ? this._currentCodeLensModel + : undefined; } } @@ -478,7 +485,7 @@ registerEditorAction(class ShowLensesInCurrentLine extends EditorAction { return; } - const model = codelensController.getModel(); + const model = await codelensController.getModel(); if (!model) { // nothing return; @@ -499,19 +506,31 @@ registerEditorAction(class ShowLensesInCurrentLine extends EditorAction { return; } - const item = await quickInputService.pick(items, { canPickMany: false }); + const item = await quickInputService.pick(items, { + canPickMany: false, + placeHolder: localize('placeHolder', "Select a command") + }); if (!item) { // Nothing picked return; } + let command = item.command; + if (model.isDisposed) { - // retry whenever the model has been disposed - return await commandService.executeCommand(this.id); + // try to find the same command again in-case the model has been re-created in the meantime + // this is a best attempt approach which shouldn't be needed because eager model re-creates + // shouldn't happen due to focus in/out anymore + const newModel = await codelensController.getModel(); + const newLens = newModel?.lenses.find(lens => lens.symbol.range.startLineNumber === lineNumber && lens.symbol.command?.title === command.title); + if (!newLens || !newLens.symbol.command) { + return; + } + command = newLens.symbol.command; } try { - await commandService.executeCommand(item.command.id, ...(item.command.arguments || [])); + await commandService.executeCommand(command.id, ...(command.arguments || [])); } catch (err) { notificationService.error(err); } From 812643de4b3679950a4e92bb19f3f2bb9c08bab4 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 1 Sep 2023 17:45:32 +0200 Subject: [PATCH 446/607] comment out not-compiling code, fyi @bpasero --- src/vs/workbench/browser/parts/editor/editorPart.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index d8250c91f15..0f868b4ad97 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -525,9 +525,9 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro locationView.focus(); } - if (options?.activate) { - this.doSetGroupActive(group); - } + // if (options?.activate) { + // this.doSetGroupActive(group); + // } return group; } From c8edc8cb2cae1871eab8ae2220c09faf77054cbb Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 1 Sep 2023 18:05:27 +0200 Subject: [PATCH 447/607] fix https://github.com/microsoft/vscode/issues/187779 --- .../contrib/suggest/browser/suggestModel.ts | 6 ++++ .../test/browser/suggestController.test.ts | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index fd8d533f594..c4e44699449 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -635,6 +635,12 @@ export class SuggestModel implements IDisposable { return; } + if (!ctx.leadingLineContent.startsWith(this._context.leadingLineContent) && !this._context.leadingLineContent.startsWith(ctx.leadingLineContent)) { + // e.g. happens when line prefix changes, e.g delete while suggest is showing + this.cancel(); + return; + } + if (getLeadingWhitespace(ctx.leadingLineContent) !== getLeadingWhitespace(this._context.leadingLineContent)) { // cancel IntelliSense when line start changes // happens when the current word gets outdented diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts index f37f02ac646..bbb32d3fe37 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts @@ -32,6 +32,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { DeleteLinesAction } from 'vs/editor/contrib/linesOperations/browser/linesOperations'; suite('SuggestController', function () { @@ -579,4 +580,38 @@ suite('SuggestController', function () { controller.acceptSelectedSuggestion(false, false); assert.strictEqual(editor.getValue(), 'for'); }); + + test('Suggest widget gets orphaned in editor #187779', async function () { + + disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { + _debugDisplayName: 'test', + provideCompletionItems(doc, pos) { + + const word = doc.getLineContent(pos.lineNumber); + const range = new Range(pos.lineNumber, 1, pos.lineNumber, pos.column); + + return { + suggestions: [{ + kind: CompletionItemKind.Text, + label: word, + insertText: word, + range + }] + }; + } + })); + + editor.setValue(`console.log(example.)\nconsole.log(EXAMPLE.not)`); + editor.setSelection(new Selection(1, 21, 1, 21)); + + const p1 = Event.toPromise(controller.model.onDidSuggest); + controller.triggerSuggest(); + + await p1; + + const p2 = Event.toPromise(controller.model.onDidCancel); + new DeleteLinesAction().run(null!, editor); + + await p2; + }); }); From a8b8e3a143bfb1530b373af80db47e2424c00897 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 1 Sep 2023 09:38:20 -0700 Subject: [PATCH 448/607] forwarding: fix log format again (#191941) Fixes #191759 --- cli/src/log.rs | 4 ++-- extensions/tunnel-forwarding/src/extension.ts | 22 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cli/src/log.rs b/cli/src/log.rs index a7561a37f6c..1180f2c82c2 100644 --- a/cli/src/log.rs +++ b/cli/src/log.rs @@ -323,8 +323,8 @@ fn format(level: Level, prefix: &str, message: &str, use_colors: bool) -> String } pub fn emit(level: Level, prefix: &str, message: &str) { - let line = format(level, prefix, message, true); - if level == Level::Trace { + let line = format(level, prefix, message, *COLORS_ENABLED); + if level == Level::Trace && *COLORS_ENABLED { print!("\x1b[2m{}\x1b[0m", line); } else { print!("{}", line); diff --git a/extensions/tunnel-forwarding/src/extension.ts b/extensions/tunnel-forwarding/src/extension.ts index 83789934df5..f6ef85e71b1 100644 --- a/extensions/tunnel-forwarding/src/extension.ts +++ b/extensions/tunnel-forwarding/src/extension.ts @@ -231,8 +231,8 @@ class TunnelProvider implements vscode.TunnelProvider { ]; this.logger.log('info', '[forwarding] starting CLI'); - const process = spawn(cliPath, args, { stdio: 'pipe' }); - this.state = { state: State.Starting, process }; + const child = spawn(cliPath, args, { stdio: 'pipe', env: { ...process.env, NO_COLOR: '1' } }); + this.state = { state: State.Starting, process: child }; const progressP = new DeferredPromise(); vscode.window.withProgress( @@ -248,29 +248,29 @@ class TunnelProvider implements vscode.TunnelProvider { ); let lastPortFormat: string | undefined; - process.on('exit', status => { + child.on('exit', status => { const msg = `[forwarding] exited with code ${status}`; this.logger.log('info', msg); progressP.complete(); // make sure to clear progress on unexpected exit - if (this.isInStateWithProcess(process)) { + if (this.isInStateWithProcess(child)) { this.state = { state: State.Error, error: msg }; } }); - process.on('error', err => { + child.on('error', err => { this.logger.log('error', `[forwarding] ${err}`); progressP.complete(); // make sure to clear progress on unexpected exit - if (this.isInStateWithProcess(process)) { + if (this.isInStateWithProcess(child)) { this.state = { state: State.Error, error: String(err) }; } }); - process.stdout + child.stdout .pipe(splitNewLines()) .on('data', line => this.logger.log('info', `[forwarding] ${line}`)) .resume(); - process.stderr + child.stderr .pipe(splitNewLines()) .on('data', line => { try { @@ -278,7 +278,7 @@ class TunnelProvider implements vscode.TunnelProvider { if (l.port_format && l.port_format !== lastPortFormat) { this.state = { state: State.Active, - portFormat: l.port_format, process, + portFormat: l.port_format, process: child, cleanupTimeout: 'cleanupTimeout' in this.state ? this.state.cleanupTimeout : undefined, }; progressP.complete(); @@ -290,8 +290,8 @@ class TunnelProvider implements vscode.TunnelProvider { .resume(); await new Promise((resolve, reject) => { - process.on('spawn', resolve); - process.on('error', reject); + child.on('spawn', resolve); + child.on('error', reject); }); } } From 55b37e271d882fe28b41de79f0a6381ffd15112e Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Fri, 1 Sep 2023 10:02:14 -0700 Subject: [PATCH 449/607] Bump distro (#192006) for the removal of semantic similarity. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1f473f744fc..4a0405868b6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.83.0", - "distro": "0a5805caff2d59440704a3bf75eebaa509be862f", + "distro": "46e7bb69af9f06de037c3d7e8f61de4a679f9ef1", "author": { "name": "Microsoft Corporation" }, From 2d502be79d044ed34fcea16eeb02cf98b789789c Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 1 Sep 2023 10:27:03 -0700 Subject: [PATCH 450/607] testing: compress single test messages in the Test Results tree view (#192011) Fixes #192010 --- .../testing/browser/testingOutputPeek.ts | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 6223c1c211f..f5e1fce889c 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -78,7 +78,6 @@ import { DetachedProcessInfo } from 'vs/workbench/contrib/terminal/browser/detac import { IDetachedTerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { flatTestItemDelimiter } from 'vs/workbench/contrib/testing/browser/explorerProjections/display'; import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay'; import * as icons from 'vs/workbench/contrib/testing/browser/icons'; import { testingPeekBorder, testingPeekHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme'; @@ -1704,15 +1703,7 @@ class TestCaseElement implements ITreeElement { private readonly task: ITestRunTask, public readonly test: TestResultItem, public readonly taskIndex: number, - ) { - for (const parent of resultItemParents(results, test)) { - if (parent !== test) { - this.description = this.description - ? parent.item.label + flatTestItemDelimiter + this.description - : parent.item.label; - } - } - } + ) { } } class TaskElement implements ITreeElement { @@ -1871,7 +1862,7 @@ class OutputPeekTree extends Disposable { return test.tasks[taskIndex].messages .map((m, messageIndex) => m.type === TestMessageType.Error - ? { element: cc.getOrCreate(m, () => new TestMessageElement(result, test, taskIndex, messageIndex)), incompressible: true } + ? { element: cc.getOrCreate(m, () => new TestMessageElement(result, test, taskIndex, messageIndex)), incompressible: false } : undefined ) .filter(isDefined); @@ -2103,8 +2094,8 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer, FuzzyScore>, _index: number, templateData: TemplateData): void { const chain = node.element.elements; const lastElement = chain[chain.length - 1]; - if (lastElement instanceof TaskElement && chain.length >= 2) { - this.doRender(chain[chain.length - 2], templateData); + if ((lastElement instanceof TaskElement || lastElement instanceof TestMessageElement) && chain.length >= 2) { + this.doRender(chain[chain.length - 2], templateData, lastElement); } else { this.doRender(lastElement, templateData); } @@ -2148,20 +2139,26 @@ class TestRunElementRenderer implements ICompressibleTreeRenderer this.doRender(element, templateData))); - this.doRenderInner(element, templateData); + templateData.elementDisposable.add( + element.onDidChange(() => this.doRender(element, templateData, subjectElement)), + ); + this.doRenderInner(element, templateData, subjectElement); } /** Called, and may be re-called, to render or re-render an element */ - private doRenderInner(element: ITreeElement, templateData: TemplateData) { - if (element.labelWithIcons) { - dom.reset(templateData.label, ...element.labelWithIcons); - } else if (element.description) { - dom.reset(templateData.label, element.label, dom.$('span.test-label-description', {}, element.description)); + private doRenderInner(element: ITreeElement, templateData: TemplateData, subjectElement: ITreeElement | undefined) { + let { label, labelWithIcons, description } = element; + if (subjectElement instanceof TestMessageElement) { + description = subjectElement.label; + } + + const descriptionElement = description ? dom.$('span.test-label-description', {}, description) : ''; + if (labelWithIcons) { + dom.reset(templateData.label, ...labelWithIcons, descriptionElement); } else { - dom.reset(templateData.label, element.label); + dom.reset(templateData.label, label, descriptionElement); } const icon = element.icon; From 81302a437b5a5ed7ce0c88e9a0a3fc5eaf6e34b4 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 1 Sep 2023 12:45:28 -0700 Subject: [PATCH 451/607] fix additional leak, comment out detector for now --- .../widget/diffEditorWidget2/outlineModel.ts | 1 + .../test/browser/inlineChatController.test.ts | 30 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts index cd12277b82c..8732bb7f0e6 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts @@ -231,6 +231,7 @@ export class OutlineModel extends TreeElement { return result._compact(); } }).finally(() => { + cts.dispose(); listener.dispose(); }); } diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 476dfbe33db..0290893971c 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -4,31 +4,30 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { equals } from 'vs/base/common/arrays'; +import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { mock } from 'vs/base/test/common/mock'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/model'; import { instantiateTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; +import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; +import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; +import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { InlineChatController, InlineChatRunOptions, State } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { IInlineChatSessionService, InlineChatSessionService } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { IInlineChatService, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { InlineChatServiceImpl } from 'vs/workbench/contrib/inlineChat/common/inlineChatServiceImpl'; import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { IModelService } from 'vs/editor/common/services/model'; -import { ITextModel } from 'vs/editor/common/model'; -import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; -import { mock } from 'vs/base/test/common/mock'; -import { Emitter, Event } from 'vs/base/common/event'; -import { equals } from 'vs/base/common/arrays'; -import { IChatAccessibilityService } from 'vs/workbench/contrib/chat/browser/chat'; -import { IChatResponseViewModel } from 'vs/workbench/contrib/chat/common/chatViewModel'; -import { IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; -import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('InteractiveChatController', function () { @@ -146,7 +145,8 @@ suite('InteractiveChatController', function () { ctrl?.dispose(); }); - ensureNoDisposablesAreLeakedInTestSuite(); + // todo: re-enable this when earlier tests are fixed + // ensureNoDisposablesAreLeakedInTestSuite(); test('creation, not showing anything', function () { for (let deadline = Date.now() + 1000; Date.now() < deadline;) { } From 0ee7a576b6c2e252266a392cac6d9c9be7b3a1d0 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 1 Sep 2023 13:17:58 -0700 Subject: [PATCH 452/607] tunnels: fix command prompt windows show up on windows machine (#192016) Fixes #190425 --- cli/src/tunnels/control_server.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cli/src/tunnels/control_server.rs b/cli/src/tunnels/control_server.rs index 6f8c1060e1f..45e0c9748ef 100644 --- a/cli/src/tunnels/control_server.rs +++ b/cli/src/tunnels/control_server.rs @@ -1021,6 +1021,9 @@ where p.current_dir(cwd); } + #[cfg(target_os = "windows")] + p.creation_flags(winapi::um::winbase::CREATE_NO_WINDOW); + let mut p = p.spawn().map_err(CodeError::ProcessSpawnFailed)?; let futs = FuturesUnordered::new(); From a6808a1534469d4cb2f52e70fedef7fcbf92e1f8 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 1 Sep 2023 13:18:17 -0700 Subject: [PATCH 453/607] testing: fix text centering in filter (#192017) Fixes #182648 --- .../workbench/contrib/testing/browser/testingExplorerFilter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts index c577c7ba942..05be7da398c 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerFilter.ts @@ -135,7 +135,7 @@ export class TestingExplorerFilter extends BaseActionViewItem { public layout(width: number) { this.input.layout(new dom.Dimension( width - /* horizontal padding */ 24 - /* editor padding */ 8 - /* filter button padding */ 22, - /* line height */ 27 - /* editor padding */ 4, + 20, // line height from suggestEnabledInput.ts )); } From 3519b130fbbd41281980d726dae3ca964305b329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=93=E8=89=AF?= <1204183885@qq.com> Date: Sat, 2 Sep 2023 04:47:01 +0800 Subject: [PATCH 454/607] fix: Close #191880, Repair command cannot be searched by keyword after localization (#191953) --- .../api/browser/mainThreadComments.ts | 4 +- .../api/browser/viewsExtensionPoint.ts | 3 +- .../test/browser/mainThreadTreeViews.test.ts | 2 +- src/vs/workbench/common/views.ts | 2 +- .../browser/preview/bulkEdit.contribution.ts | 2 +- .../browser/chatContributionServiceImpl.ts | 2 +- .../comments/browser/commentsTreeViewer.ts | 1 + .../test/browser/commentsView.test.ts | 2 +- .../debug/browser/debug.contribution.ts | 2 +- .../browser/editSessions.contribution.ts | 4 +- .../editSessions/common/editSessions.ts | 1 + .../contrib/files/browser/explorerViewlet.ts | 2 +- .../markers/browser/markers.contribution.ts | 2 +- .../contrib/markers/browser/messages.ts | 1 + .../output/browser/output.contribution.ts | 2 +- .../contrib/remote/browser/remoteExplorer.ts | 2 +- .../contrib/scm/browser/scm.contribution.ts | 2 +- .../terminal/browser/terminal.contribution.ts | 2 +- .../testing/browser/testing.contribution.ts | 4 +- .../userDataSync/browser/userDataSync.ts | 4 +- .../userDataSync/common/userDataSync.ts | 1 + .../views/browser/viewDescriptorService.ts | 2 +- .../test/browser/viewContainerModel.test.ts | 44 +++++++++---------- .../browser/viewDescriptorService.test.ts | 16 +++---- 24 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 29f7b613e5a..ab886d7dc8c 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -16,7 +16,7 @@ import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/ext import { ICommentController, ICommentInfo, ICommentService, INotebookCommentInfo } from 'vs/workbench/contrib/comments/browser/commentService'; import { CommentsPanel } from 'vs/workbench/contrib/comments/browser/commentsView'; import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol'; -import { COMMENTS_VIEW_ID, COMMENTS_VIEW_STORAGE_ID, COMMENTS_VIEW_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; +import { COMMENTS_VIEW_ID, COMMENTS_VIEW_STORAGE_ID, COMMENTS_VIEW_TITLE, COMMENTS_VIEW_ORIGINAL_TITLE } from 'vs/workbench/contrib/comments/browser/commentsTreeViewer'; import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -596,7 +596,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments if (!commentsViewAlreadyRegistered) { const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: COMMENTS_VIEW_ID, - title: COMMENTS_VIEW_TITLE, + title: { value: COMMENTS_VIEW_TITLE, original: COMMENTS_VIEW_ORIGINAL_TITLE }, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true }]), storageId: COMMENTS_VIEW_STORAGE_ID, hideIfEmpty: true, diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index f97885e8e02..23d64b26b76 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -435,7 +435,8 @@ class ViewsExtensionHandler implements IWorkbenchContribution { viewContainer = this.viewContainersRegistry.registerViewContainer({ id, - title, extensionId, + title: { value: title, original: title }, + extensionId, ctorDescriptor: new SyncDescriptor( ViewPaneContainer, [id, { mergeViewWithContainerWhenSingleView: true }] diff --git a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts index 421d9eecd4a..f796cae8ed2 100644 --- a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts @@ -52,7 +52,7 @@ suite('MainThreadHostTreeView', function () { const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposables); const viewDescriptorService = instantiationService.createInstance(ViewDescriptorService); instantiationService.stub(IViewDescriptorService, viewDescriptorService); - container = Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ id: 'testContainer', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ id: 'testContainer', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const viewDescriptor: ITreeViewDescriptor = { id: testTreeViewId, ctorDescriptor: null!, diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 07578cb75b6..8633aac784a 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -75,7 +75,7 @@ export interface IViewContainerDescriptor { /** * The title of the view container */ - readonly title: ILocalizedString | string; + readonly title: ILocalizedString; /** * Icon representation of the View container diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts index 2c3c2a6d72e..ff63502983b 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEdit.contribution.ts @@ -326,7 +326,7 @@ const refactorPreviewViewIcon = registerIcon('refactor-preview-view-icon', Codic const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: BulkEditPane.ID, - title: localize('panel', "Refactor Preview"), + title: { value: localize('panel', "Refactor Preview"), original: 'Refactor Preview' }, hideIfEmpty: true, ctorDescriptor: new SyncDescriptor( ViewPaneContainer, diff --git a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts index d10beb91120..aa774249863 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContributionServiceImpl.ts @@ -113,7 +113,7 @@ export class ChatContributionService implements IChatContributionService { const viewContainerId = CHAT_SIDEBAR_PANEL_ID + '.' + providerDescriptor.id; const viewContainer: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: viewContainerId, - title, + title: { value: title, original: 'Chat' }, icon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [viewContainerId, { mergeViewWithContainerWhenSingleView: true }]), storageId: viewContainerId, diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index c0e24d570e5..890437bb65a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -33,6 +33,7 @@ import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; export const COMMENTS_VIEW_ID = 'workbench.panel.comments'; export const COMMENTS_VIEW_STORAGE_ID = 'Comments'; +export const COMMENTS_VIEW_ORIGINAL_TITLE = 'Comments'; export const COMMENTS_VIEW_TITLE = nls.localize('comments.view.title', "Comments"); interface IResourceTemplateData { diff --git a/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts b/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts index 7dbdff3d855..83d2bf1ccea 100644 --- a/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts +++ b/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts @@ -54,7 +54,7 @@ export class TestViewDescriptorService implements Partial(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: DEBUG_PANEL_ID, - title: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, "Debug Console"), + title: { value: nls.localize({ comment: ['Debug is a noun in this context, not a verb.'], key: 'debugPanel' }, "Debug Console"), original: 'Debug Console' }, icon: icons.debugConsoleViewIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [DEBUG_PANEL_ID, { mergeViewWithContainerWhenSingleView: true }]), storageId: DEBUG_PANEL_ID, diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index 2ac22f1d0d7..9495543aad6 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -10,7 +10,7 @@ import { ILifecycleService, LifecyclePhase, ShutdownReason } from 'vs/workbench/ import { Action2, IAction2Options, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; -import { IEditSessionsStorageService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EDIT_SESSIONS_CONTAINER_ID, EditSessionSchemaVersion, IEditSessionsLogService, EDIT_SESSIONS_VIEW_ICON, EDIT_SESSIONS_TITLE, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_DATA_VIEW_ID, decodeEditSessionFileContent, hashedEditSessionId, editSessionsLogId, EDIT_SESSIONS_PENDING } from 'vs/workbench/contrib/editSessions/common/editSessions'; +import { IEditSessionsStorageService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EDIT_SESSIONS_CONTAINER_ID, EditSessionSchemaVersion, IEditSessionsLogService, EDIT_SESSIONS_VIEW_ICON, EDIT_SESSIONS_TITLE, EDIT_SESSIONS_ORIGINAL_TITLE, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_DATA_VIEW_ID, decodeEditSessionFileContent, hashedEditSessionId, editSessionsLogId, EDIT_SESSIONS_PENDING } from 'vs/workbench/contrib/editSessions/common/editSessions'; import { ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -274,7 +274,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo const container = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer( { id: EDIT_SESSIONS_CONTAINER_ID, - title: EDIT_SESSIONS_TITLE, + title: { value: EDIT_SESSIONS_TITLE, original: EDIT_SESSIONS_ORIGINAL_TITLE }, ctorDescriptor: new SyncDescriptor( ViewPaneContainer, [EDIT_SESSIONS_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }] diff --git a/src/vs/workbench/contrib/editSessions/common/editSessions.ts b/src/vs/workbench/contrib/editSessions/common/editSessions.ts index 53c39076411..4cb53dc45f7 100644 --- a/src/vs/workbench/contrib/editSessions/common/editSessions.ts +++ b/src/vs/workbench/contrib/editSessions/common/editSessions.ts @@ -98,6 +98,7 @@ export const EDIT_SESSIONS_PENDING = new RawContextKey(EDIT_SESSIONS_PE export const EDIT_SESSIONS_CONTAINER_ID = 'workbench.view.editSessions'; export const EDIT_SESSIONS_DATA_VIEW_ID = 'workbench.views.editSessions.data'; +export const EDIT_SESSIONS_ORIGINAL_TITLE = 'Cloud Changes'; export const EDIT_SESSIONS_TITLE = localize('cloud changes', 'Cloud Changes'); export const EDIT_SESSIONS_VIEW_ICON = registerIcon('edit-sessions-view-icon', Codicon.cloudDownload, localize('editSessionViewIcon', 'View icon of the cloud changes view.')); diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index 602ffa99968..af0be566a8b 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -252,7 +252,7 @@ const viewContainerRegistry = Registry.as(Extensions.Vi */ export const VIEW_CONTAINER: ViewContainer = viewContainerRegistry.registerViewContainer({ id: VIEWLET_ID, - title: localize('explore', "Explorer"), + title: { value: localize('explore', "Explorer"), original: 'Explorer' }, ctorDescriptor: new SyncDescriptor(ExplorerViewPaneContainer), storageId: 'workbench.explorer.views.state', icon: explorerViewIcon, diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 275a84b8c9a..ef2f37cee8c 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -128,7 +128,7 @@ const markersViewIcon = registerIcon('markers-view-icon', Codicon.warning, local // markers view container const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: Markers.MARKERS_CONTAINER_ID, - title: Messages.MARKERS_PANEL_TITLE_PROBLEMS, + title: { value: Messages.MARKERS_PANEL_TITLE_PROBLEMS, original: Messages.MARKERS_PANEL_ORIGINAL_TITLE_PROBLEMS }, icon: markersViewIcon, hideIfEmpty: true, order: 0, diff --git a/src/vs/workbench/contrib/markers/browser/messages.ts b/src/vs/workbench/contrib/markers/browser/messages.ts index 7ff0582f02f..767485b3692 100644 --- a/src/vs/workbench/contrib/markers/browser/messages.ts +++ b/src/vs/workbench/contrib/markers/browser/messages.ts @@ -21,6 +21,7 @@ export default class Messages { public static PROBLEMS_PANEL_CONFIGURATION_COMPARE_ORDER_SEVERITY: string = nls.localize('problems.panel.configuration.compareOrder.severity', "Navigate problems ordered by severity"); public static PROBLEMS_PANEL_CONFIGURATION_COMPARE_ORDER_POSITION: string = nls.localize('problems.panel.configuration.compareOrder.position', "Navigate problems ordered by position"); + public static MARKERS_PANEL_ORIGINAL_TITLE_PROBLEMS: string = 'Problems'; public static MARKERS_PANEL_TITLE_PROBLEMS: string = nls.localize('markers.panel.title.problems', "Problems"); public static MARKERS_PANEL_NO_PROBLEMS_BUILT: string = nls.localize('markers.panel.no.problems.build', "No problems have been detected in the workspace."); diff --git a/src/vs/workbench/contrib/output/browser/output.contribution.ts b/src/vs/workbench/contrib/output/browser/output.contribution.ts index 87d04e5fb83..44b292ed5b1 100644 --- a/src/vs/workbench/contrib/output/browser/output.contribution.ts +++ b/src/vs/workbench/contrib/output/browser/output.contribution.ts @@ -54,7 +54,7 @@ ModesRegistry.registerLanguage({ const outputViewIcon = registerIcon('output-view-icon', Codicon.output, nls.localize('outputViewIcon', 'View icon of the output view.')); const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: OUTPUT_VIEW_ID, - title: nls.localize('output', "Output"), + title: { value: nls.localize('output', "Output"), original: 'Output' }, icon: outputViewIcon, order: 1, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [OUTPUT_VIEW_ID, { mergeViewWithContainerWhenSingleView: true }]), diff --git a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts index fef42ab467a..f3785c75aed 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteExplorer.ts @@ -61,7 +61,7 @@ export class ForwardedPortsView extends Disposable implements IWorkbenchContribu private async getViewContainer(): Promise { return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ id: TUNNEL_VIEW_CONTAINER_ID, - title: nls.localize('ports', "Ports"), + title: { value: nls.localize('ports', "Ports"), original: 'Ports' }, icon: portsViewIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [TUNNEL_VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }]), storageId: TUNNEL_VIEW_CONTAINER_ID, diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index e034cf09200..f78bcd673ab 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -47,7 +47,7 @@ const sourceControlViewIcon = registerIcon('source-control-view-icon', Codicon.s const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, - title: localize('source control', "Source Control"), + title: { value: localize('source control', "Source Control"), original: 'Source Control' }, ctorDescriptor: new SyncDescriptor(SCMViewPaneContainer), storageId: 'workbench.scm.views.state', icon: sourceControlViewIcon, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index eea627b0e88..1bb341555ef 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -123,7 +123,7 @@ Registry.as(DragAndDropExtensions.DragAndDropC // Register views const VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: TERMINAL_VIEW_ID, - title: nls.localize('terminal', "Terminal"), + title: { value: nls.localize('terminal', "Terminal"), original: 'Terminal' }, icon: terminalViewIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [TERMINAL_VIEW_ID, { mergeViewWithContainerWhenSingleView: true }]), storageId: TERMINAL_VIEW_ID, diff --git a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts index cac9c0c5022..f628cbf59ec 100644 --- a/src/vs/workbench/contrib/testing/browser/testing.contribution.ts +++ b/src/vs/workbench/contrib/testing/browser/testing.contribution.ts @@ -56,7 +56,7 @@ registerSingleton(ITestingDecorationsService, TestingDecorationService, Instanti const viewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: Testing.ViewletId, - title: localize('test', "Testing"), + title: { value: localize('test', "Testing"), original: 'Testing' }, ctorDescriptor: new SyncDescriptor(TestingViewPaneContainer), icon: testingViewIcon, alwaysUseContainerInfo: true, @@ -74,7 +74,7 @@ const viewContainer = Registry.as(ViewContainerExtensio const testResultsViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: Testing.ResultsPanelId, - title: localize('testResultsPanelName', "Test Results"), + title: { value: localize('testResultsPanelName', "Test Results"), original: 'Test Results' }, icon: testingResultsIcon, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [Testing.ResultsPanelId, { mergeViewWithContainerWhenSingleView: true }]), hideIfEmpty: true, diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 870402650e2..e50bd3e8559 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -43,7 +43,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewContainerLocation, IViewContainersRegistry, Extensions, ViewContainer } from 'vs/workbench/common/views'; import { UserDataSyncDataViews } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncViews'; -import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_VIEW_ICON, CONTEXT_HAS_CONFLICTS } from 'vs/workbench/services/userDataSync/common/userDataSync'; +import { IUserDataSyncWorkbenchService, getSyncAreaLabel, AccountStatus, CONTEXT_SYNC_STATE, CONTEXT_SYNC_ENABLEMENT, CONTEXT_ACCOUNT_STATE, CONFIGURE_SYNC_COMMAND_ID, SHOW_SYNC_LOG_COMMAND_ID, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_ORIGINAL_TITLE, SYNC_VIEW_ICON, CONTEXT_HAS_CONFLICTS } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { Codicon } from 'vs/base/common/codicons'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; @@ -1134,7 +1134,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo return Registry.as(Extensions.ViewContainersRegistry).registerViewContainer( { id: SYNC_VIEW_CONTAINER_ID, - title: SYNC_TITLE, + title: { value: SYNC_TITLE, original: SYNC_ORIGINAL_TITLE }, ctorDescriptor: new SyncDescriptor( ViewPaneContainer, [SYNC_VIEW_CONTAINER_ID, { mergeViewWithContainerWhenSingleView: true }] diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts index 9a709322d34..1cfcc42cc02 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts @@ -69,6 +69,7 @@ export interface IUserDataSyncConflictsView extends IView { open(conflict: IResourcePreview): Promise; } +export const SYNC_ORIGINAL_TITLE = 'Settings Sync'; export const SYNC_TITLE = localize('sync category', "Settings Sync"); export const SYNC_VIEW_ICON = registerIcon('settings-sync-view-icon', Codicon.sync, localize('syncViewIcon', 'View icon of the Settings Sync view.')); diff --git a/src/vs/workbench/services/views/browser/viewDescriptorService.ts b/src/vs/workbench/services/views/browser/viewDescriptorService.ts index b802ba5eab5..248e5db87be 100644 --- a/src/vs/workbench/services/views/browser/viewDescriptorService.ts +++ b/src/vs/workbench/services/views/browser/viewDescriptorService.ts @@ -490,7 +490,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor const container = this.viewContainersRegistry.registerViewContainer({ id, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [id, { mergeViewWithContainerWhenSingleView: true }]), - title: id, // we don't want to see this so using id + title: { value: id, original: id }, // we don't want to see this so using id icon: location === ViewContainerLocation.Sidebar ? defaultViewIcon : undefined, storageId: getViewContainerStorageId(id), hideIfEmpty: true diff --git a/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts b/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts index e445f4a41b9..57723675e03 100644 --- a/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts +++ b/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts @@ -64,13 +64,13 @@ suite('ViewContainerModel', () => { }); test('empty model', function () { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); assert.strictEqual(testObject.visibleViewDescriptors.length, 0); }); test('register/unregister', () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -97,7 +97,7 @@ suite('ViewContainerModel', () => { }); test('when contexts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); assert.strictEqual(testObject.visibleViewDescriptors.length, 0); @@ -141,7 +141,7 @@ suite('ViewContainerModel', () => { })); test('when contexts - multiple', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' }; @@ -164,7 +164,7 @@ suite('ViewContainerModel', () => { })); test('when contexts - multiple 2', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', when: ContextKeyExpr.equals('showview1', true) }; @@ -187,7 +187,7 @@ suite('ViewContainerModel', () => { })); test('setVisible', () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1', canToggleVisibility: true }; @@ -232,7 +232,7 @@ suite('ViewContainerModel', () => { }); test('move', () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const view1: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, name: 'Test View 1' }; @@ -262,7 +262,7 @@ suite('ViewContainerModel', () => { test('view states', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.PROFILE, StorageTarget.MACHINE); - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -282,7 +282,7 @@ suite('ViewContainerModel', () => { test('view states and when contexts', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.PROFILE, StorageTarget.MACHINE); - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -312,7 +312,7 @@ suite('ViewContainerModel', () => { test('view states and when contexts multiple views', () => runWithFakedTimers({ useFakeTimers: true }, async () => { storageService.store(`${container.id}.state.hidden`, JSON.stringify([{ id: 'view1', isHidden: true }]), StorageScope.PROFILE, StorageTarget.MACHINE); - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -357,7 +357,7 @@ suite('ViewContainerModel', () => { })); test('remove event is not triggered if view was hidden and removed', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor: IViewDescriptor = { @@ -387,7 +387,7 @@ suite('ViewContainerModel', () => { })); test('add event is not triggered if view was set visible (when visible) and not active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor: IViewDescriptor = { @@ -414,7 +414,7 @@ suite('ViewContainerModel', () => { })); test('remove event is not triggered if view was hidden and not active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor: IViewDescriptor = { @@ -441,7 +441,7 @@ suite('ViewContainerModel', () => { })); test('add event is not triggered if view was set visible (when not visible) and not active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor: IViewDescriptor = { @@ -472,7 +472,7 @@ suite('ViewContainerModel', () => { })); test('added view descriptors are in ascending order in the event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); @@ -523,7 +523,7 @@ suite('ViewContainerModel', () => { })); test('add event is triggered only once when view is set visible while it is set active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor: IViewDescriptor = { @@ -554,7 +554,7 @@ suite('ViewContainerModel', () => { })); test('add event is not triggered only when view is set hidden while it is set active', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor: IViewDescriptor = { @@ -583,7 +583,7 @@ suite('ViewContainerModel', () => { })); test('#142087: view descriptor visibility is not reset', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const viewDescriptor: IViewDescriptor = { id: 'view1', @@ -606,7 +606,7 @@ suite('ViewContainerModel', () => { })); test('remove event is triggered properly if mutliple views are hidden at the same time', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor1: IViewDescriptor = { @@ -664,7 +664,7 @@ suite('ViewContainerModel', () => { })); test('add event is triggered properly if mutliple views are hidden at the same time', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor1: IViewDescriptor = { @@ -732,7 +732,7 @@ suite('ViewContainerModel', () => { })); test('add and remove events are triggered properly if mutliple views are hidden and added at the same time', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const testObject = viewDescriptorService.getViewContainerModel(container); const target = disposableStore.add(new ViewDescriptorSequence(testObject)); const viewDescriptor1: IViewDescriptor = { @@ -809,7 +809,7 @@ suite('ViewContainerModel', () => { })); test('newly added view descriptor is hidden if it was toggled hidden in storage before adding', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + container = ViewContainerRegistry.registerViewContainer({ id: 'test', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const viewDescriptor: IViewDescriptor = { id: 'view1', ctorDescriptor: null!, diff --git a/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts b/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts index 74b4b56185d..4a1c167c07f 100644 --- a/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts +++ b/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts @@ -21,8 +21,8 @@ import { compare } from 'vs/base/common/strings'; const ViewsRegistry = Registry.as(ViewContainerExtensions.ViewsRegistry); const ViewContainersRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); const viewContainerIdPrefix = 'testViewContainer'; -const sidebarContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); -const panelContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Panel); +const sidebarContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); +const panelContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Panel); suite('ViewDescriptorService', () => { @@ -331,7 +331,7 @@ suite('ViewDescriptorService', () => { test('initialize with custom locations', async function () { const storageService = instantiationService.get(IStorageService); - const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const generateViewContainer1 = `workbench.views.service.${ViewContainerLocationToString(ViewContainerLocation.Sidebar)}.${generateUuid()}`; const viewsCustomizations = { viewContainerLocations: { @@ -390,7 +390,7 @@ suite('ViewDescriptorService', () => { test('storage change', async function () { const testObject = aViewDescriptorService(); - const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const generateViewContainer1 = `workbench.views.service.${ViewContainerLocationToString(ViewContainerLocation.Sidebar)}.${generateUuid()}`; const viewDescriptors: IViewDescriptor[] = [ @@ -525,7 +525,7 @@ suite('ViewDescriptorService', () => { test('custom locations take precedence when default view container of views change', async function () { const storageService = instantiationService.get(IStorageService); - const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const generateViewContainer1 = `workbench.views.service.${ViewContainerLocationToString(ViewContainerLocation.Sidebar)}.${generateUuid()}`; const viewsCustomizations = { viewContainerLocations: { @@ -587,7 +587,7 @@ suite('ViewDescriptorService', () => { test('view containers with not existing views are not removed from customizations', async function () { const storageService = instantiationService.get(IStorageService); - const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + const viewContainer1 = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const generateViewContainer1 = `workbench.views.service.${ViewContainerLocationToString(ViewContainerLocation.Sidebar)}.${generateUuid()}`; const viewsCustomizations = { viewContainerLocations: { @@ -637,7 +637,7 @@ suite('ViewDescriptorService', () => { }; storageService.store('views.customizations', JSON.stringify(viewsCustomizations), StorageScope.PROFILE, StorageTarget.USER); - const viewContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + const viewContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const viewDescriptors: IViewDescriptor[] = [ { id: 'view1', @@ -669,7 +669,7 @@ suite('ViewDescriptorService', () => { const storageService = instantiationService.get(IStorageService); const testObject = aViewDescriptorService(); - const viewContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: 'test', ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); + const viewContainer = ViewContainersRegistry.registerViewContainer({ id: `${viewContainerIdPrefix}-${generateUuid()}`, title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const viewDescriptors: IViewDescriptor[] = [ { id: 'view1', From 5d10e5ac5efafbeb5425b2b979d5ff584e13cba9 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Fri, 1 Sep 2023 15:14:50 -0700 Subject: [PATCH 455/607] rm debug code --- .../contrib/inlineChat/test/browser/inlineChatController.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 0290893971c..e31040fecc5 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -149,7 +149,6 @@ suite('InteractiveChatController', function () { // ensureNoDisposablesAreLeakedInTestSuite(); test('creation, not showing anything', function () { - for (let deadline = Date.now() + 1000; Date.now() < deadline;) { } ctrl = instaService.createInstance(TestController, editor); assert.ok(ctrl); assert.strictEqual(ctrl.getWidgetPosition(), undefined); From 6db3388f33041cf539d51a679dacdb4fb282b333 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 4 Sep 2023 09:36:10 +0200 Subject: [PATCH 456/607] mark tests as skipped, remove fix attempt --- src/vs/editor/contrib/suggest/browser/suggestModel.ts | 6 ------ .../contrib/suggest/test/browser/suggestController.test.ts | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index c4e44699449..fd8d533f594 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -635,12 +635,6 @@ export class SuggestModel implements IDisposable { return; } - if (!ctx.leadingLineContent.startsWith(this._context.leadingLineContent) && !this._context.leadingLineContent.startsWith(ctx.leadingLineContent)) { - // e.g. happens when line prefix changes, e.g delete while suggest is showing - this.cancel(); - return; - } - if (getLeadingWhitespace(ctx.leadingLineContent) !== getLeadingWhitespace(this._context.leadingLineContent)) { // cancel IntelliSense when line start changes // happens when the current word gets outdented diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts index bbb32d3fe37..4e8202abc64 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts @@ -581,7 +581,7 @@ suite('SuggestController', function () { assert.strictEqual(editor.getValue(), 'for'); }); - test('Suggest widget gets orphaned in editor #187779', async function () { + test.skip('Suggest widget gets orphaned in editor #187779', async function () { disposables.add(languageFeaturesService.completionProvider.register({ scheme: 'test-ctrl' }, { _debugDisplayName: 'test', From 83120dbf0646e61a219febe51e63ddd88e149cbd Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 4 Sep 2023 09:44:42 +0200 Subject: [PATCH 457/607] chore - tackle todo for type converter --- .../workbench/api/common/extHostInlineChat.ts | 19 +------------------ .../api/common/extHostTypeConverters.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/common/extHostInlineChat.ts b/src/vs/workbench/api/common/extHostInlineChat.ts index 32161feb629..d46e67243fc 100644 --- a/src/vs/workbench/api/common/extHostInlineChat.ts +++ b/src/vs/workbench/api/common/extHostInlineChat.ts @@ -223,25 +223,8 @@ export class ExtHostInteractiveEditor implements ExtHostInlineChatShape { const entry = this._inputProvider.get(handle); const sessionData = this._inputSessions.get(sessionId); const response = sessionData?.responses[responseId]; - if (entry && response) { - // todo@jrieken move to type converter - let apiKind: extHostTypes.InteractiveEditorResponseFeedbackKind; - switch (kind) { - case InlineChatResponseFeedbackKind.Helpful: - apiKind = extHostTypes.InteractiveEditorResponseFeedbackKind.Helpful; - break; - case InlineChatResponseFeedbackKind.Unhelpful: - apiKind = extHostTypes.InteractiveEditorResponseFeedbackKind.Unhelpful; - break; - case InlineChatResponseFeedbackKind.Undone: - apiKind = extHostTypes.InteractiveEditorResponseFeedbackKind.Undone; - break; - case InlineChatResponseFeedbackKind.Accepted: - apiKind = extHostTypes.InteractiveEditorResponseFeedbackKind.Accepted; - break; - } - + const apiKind = typeConvert.InteractiveEditorResponseFeedbackKind.to(kind); entry.provider.handleInteractiveEditorResponseFeedback?.(sessionData.session, response, apiKind); } } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index d5939c95e25..06f595fcb7b 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -46,6 +46,7 @@ import type * as vscode from 'vscode'; import * as types from './extHostTypes'; import * as chatProvider from 'vs/workbench/contrib/chat/common/chatProvider'; import { IChatRequestVariableValue } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { InlineChatResponseFeedbackKind } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; export namespace Command { @@ -2267,6 +2268,22 @@ export namespace ChatVariableLevel { } } +export namespace InteractiveEditorResponseFeedbackKind { + + export function to(kind: InlineChatResponseFeedbackKind): vscode.InteractiveEditorResponseFeedbackKind { + switch (kind) { + case InlineChatResponseFeedbackKind.Helpful: + return types.InteractiveEditorResponseFeedbackKind.Helpful; + case InlineChatResponseFeedbackKind.Unhelpful: + return types.InteractiveEditorResponseFeedbackKind.Unhelpful; + case InlineChatResponseFeedbackKind.Undone: + return types.InteractiveEditorResponseFeedbackKind.Undone; + case InlineChatResponseFeedbackKind.Accepted: + return types.InteractiveEditorResponseFeedbackKind.Accepted; + } + } +} + export namespace TerminalQuickFix { export function from(quickFix: vscode.TerminalQuickFixExecuteTerminalCommand | vscode.TerminalQuickFixOpener | vscode.Command, converter: Command.ICommandsConverter, disposables: DisposableStore): extHostProtocol.ITerminalQuickFixExecuteTerminalCommandDto | extHostProtocol.ITerminalQuickFixOpenerDto | extHostProtocol.ICommandDto | undefined { From 5fc89eb71380e7850e07a8a2cf8827421daab404 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 09:45:00 +0200 Subject: [PATCH 458/607] Small diff editor refactoring --- .../diffEditorWidget2/diffEditorEditors.ts | 7 ++- .../diffEditorWidget2/diffEditorOptions.ts | 2 +- .../diffEditorWidget2/diffEditorViewModel.ts | 21 ++++--- .../diffEditorWidget2/diffEditorWidget2.ts | 6 +- ...nges.ts => hideUnchangedRegionsFeature.ts} | 61 +++++++++++-------- .../browser/widget/diffEditorWidget2/utils.ts | 7 +++ .../browser/widget/diffEditorWidget2.test.ts | 2 +- 7 files changed, 63 insertions(+), 43 deletions(-) rename src/vs/editor/browser/widget/diffEditorWidget2/{unchangedRanges.ts => hideUnchangedRegionsFeature.ts} (91%) diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index 254ce84d2be..2294861e7af 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IReader, autorunHandleChanges } from 'vs/base/common/observable'; +import { IObservable, IReader, autorunHandleChanges, observableFromEvent } from 'vs/base/common/observable'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; @@ -16,6 +16,7 @@ import { localize } from 'vs/nls'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DiffEditorOptions } from './diffEditorOptions'; +import { ITextModel } from 'vs/editor/common/model'; export class DiffEditorEditors extends Disposable { public readonly modified: CodeEditorWidget; @@ -24,6 +25,8 @@ export class DiffEditorEditors extends Disposable { private readonly _onDidContentSizeChange = this._register(new Emitter()); public get onDidContentSizeChange() { return this._onDidContentSizeChange.event; } + public readonly modifiedModel: IObservable; + constructor( private readonly originalEditorElement: HTMLElement, private readonly modifiedEditorElement: HTMLElement, @@ -38,6 +41,8 @@ export class DiffEditorEditors extends Disposable { this.original = this._register(this._createLeftHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.originalEditor || {})); this.modified = this._register(this._createRightHandSideEditor(_options.editorOptions.get(), codeEditorWidgetOptions.modifiedEditor || {})); + this.modifiedModel = observableFromEvent(this.modified.onDidChangeModel, () => this.modified.getModel()); + this._register(autorunHandleChanges({ createEmptyChangeSummary: () => ({} as IDiffEditorConstructionOptions), handleChange: (ctx, changeSummary) => { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts index ffb76330146..066eefdb1e5 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts @@ -54,7 +54,7 @@ export class DiffEditorOptions { public readonly hideUnchangedRegions = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.enabled!); public readonly hideUnchangedRegionsRevealLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.revealLineCount!); public readonly hideUnchangedRegionsContextLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.contextLineCount!); - public readonly hideUnchangedRegionsminimumLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.minimumLineCount!); + public readonly hideUnchangedRegionsMinimumLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.minimumLineCount!); public updateOptions(changedOptions: IDiffEditorOptions): void { const newDiffEditorOptions = validateDiffEditorOptions(changedOptions, this._options.get()); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 143b1834b66..dd4714d7173 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -82,7 +82,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo result.changes, model.original.getLineCount(), model.modified.getLineCount(), - this._options.hideUnchangedRegionsminimumLineCount.read(reader), + this._options.hideUnchangedRegionsMinimumLineCount.read(reader), this._options.hideUnchangedRegionsContextLineCount.read(reader), ); @@ -99,18 +99,18 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo const originalDecorationIds = model.original.deltaDecorations( lastUnchangedRegions.originalDecorationIds, - newUnchangedRegions.map(r => ({ range: r.originalRange.toInclusiveRange()!, options: { description: 'unchanged' } })) + newUnchangedRegions.map(r => ({ range: r.originalUnchangedRange.toInclusiveRange()!, options: { description: 'unchanged' } })) ); const modifiedDecorationIds = model.modified.deltaDecorations( lastUnchangedRegions.modifiedDecorationIds, - newUnchangedRegions.map(r => ({ range: r.modifiedRange.toInclusiveRange()!, options: { description: 'unchanged' } })) + newUnchangedRegions.map(r => ({ range: r.modifiedUnchangedRange.toInclusiveRange()!, options: { description: 'unchanged' } })) ); for (const r of newUnchangedRegions) { for (let i = 0; i < lastUnchangedRegions.regions.length; i++) { - if (r.originalRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]) - && r.modifiedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) { + if (r.originalUnchangedRange.intersectsStrict(lastUnchangedRegionsOrigRanges[i]) + && r.modifiedUnchangedRange.intersectsStrict(lastUnchangedRegionsModRanges[i])) { r.setHiddenModifiedRange(lastUnchangedRegions.regions[i].getHiddenModifiedRange(undefined), tx); break; } @@ -169,7 +169,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo /** @description compute diff */ // So that they get recomputed when these settings change - this._options.hideUnchangedRegionsminimumLineCount.read(reader); + this._options.hideUnchangedRegionsMinimumLineCount.read(reader); this._options.hideUnchangedRegionsContextLineCount.read(reader); debouncer.cancel(); @@ -260,7 +260,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo transaction(tx => { for (const r of regions.regions) { for (const range of ranges) { - if (r.modifiedRange.intersect(range)) { + if (r.modifiedUnchangedRange.intersect(range)) { r.setHiddenModifiedRange(range, tx); break; } @@ -357,11 +357,11 @@ export class UnchangedRegion { return result; } - public get originalRange(): LineRange { + public get originalUnchangedRange(): LineRange { return LineRange.ofLength(this.originalLineNumber, this.lineCount); } - public get modifiedRange(): LineRange { + public get modifiedUnchangedRange(): LineRange { return LineRange.ofLength(this.modifiedLineNumber, this.lineCount); } @@ -371,7 +371,8 @@ export class UnchangedRegion { private readonly _visibleLineCountBottom = observableValue('visibleLineCountBottom', 0); public readonly visibleLineCountBottom: ISettableObservable = this._visibleLineCountBottom; - private readonly _shouldHideControls = derived(reader => /** @description isVisible */ this.visibleLineCountTop.read(reader) + this.visibleLineCountBottom.read(reader) === this.lineCount && !this.isDragged.read(reader)); + private readonly _shouldHideControls = derived(reader => /** @description isVisible */ + this.visibleLineCountTop.read(reader) + this.visibleLineCountBottom.read(reader) === this.lineCount && !this.isDragged.read(reader)); public readonly isDragged = observableValue('isDragged', false); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 94af3098934..40094652891 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -21,7 +21,7 @@ import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditorWidget2/diffE import { ViewZoneManager } from 'vs/editor/browser/widget/diffEditorWidget2/lineAlignment'; import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines'; import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart'; -import { UnchangedRangesFeature } from 'vs/editor/browser/widget/diffEditorWidget2/unchangedRanges'; +import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature'; import { CSSStyle, ObservableElementSizeObserver, applyStyle, readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -68,7 +68,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly _sash: IObservable; private readonly _boundarySashes = observableValue('boundarySashes', undefined); - private unchangedRangesFeature!: UnchangedRangesFeature; + private unchangedRangesFeature!: HideUnchangedRegionsFeature; private _accessibleDiffViewerShouldBeVisible = observableValue('accessibleDiffViewerShouldBeVisible', false); private _accessibleDiffViewerVisible = derived(reader => @@ -162,7 +162,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { this._register(autorunWithStore((reader, store) => { /** @description UnchangedRangesFeature */ this.unchangedRangesFeature = store.add( - this._instantiationService.createInstance(readHotReloadableExport(UnchangedRangesFeature, reader), this._editors, this._diffModel, this._options) + this._instantiationService.createInstance(readHotReloadableExport(HideUnchangedRegionsFeature, reader), this._editors, this._diffModel, this._options) ); })); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts b/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts similarity index 91% rename from src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts rename to src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts index a61dc81803b..6c7851b9416 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/unchangedRanges.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts @@ -6,7 +6,6 @@ import { $, addDisposableListener, h, reset } from 'vs/base/browser/dom'; import { renderIcon, renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { compareBy, numberComparator, reverseOrder } from 'vs/base/common/arrays'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Event } from 'vs/base/common/event'; import { MarkdownString } from 'vs/base/common/htmlContent'; @@ -19,7 +18,7 @@ import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/di import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions'; import { DiffEditorViewModel, UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; import { OutlineModel } from 'vs/editor/browser/widget/diffEditorWidget2/outlineModel'; -import { PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { DisposableCancellationTokenSource, PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; @@ -30,14 +29,12 @@ import { IModelDecorationOptions, IModelDeltaDecoration, ITextModel } from 'vs/e import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { localize } from 'vs/nls'; -export class UnchangedRangesFeature extends Disposable { +export class HideUnchangedRegionsFeature extends Disposable { private _isUpdatingViewZones = false; public get isUpdatingViewZones(): boolean { return this._isUpdatingViewZones; } - private readonly _modifiedModel = observableFromEvent(this._editors.modified.onDidChangeModel, () => this._editors.modified.getModel()); - private readonly _modifiedOutlineSource = derivedWithStore('modified outline source', (reader, store) => { - const m = this._modifiedModel.read(reader); + const m = this._editors.modifiedModel.read(reader); if (!m) { return undefined; } return store.add(new OutlineSource(this._languageFeaturesService, m)); }); @@ -77,13 +74,13 @@ export class UnchangedRangesFeature extends Disposable { const unchangedRegions = this._diffModel.map((m, reader) => m?.diff.read(reader)?.mappings.length === 0 ? [] : m?.unchangedRegions.read(reader) ?? []); const viewZones = derivedWithStore('view zones', (reader, store) => { + const modifiedOutlineSource = this._modifiedOutlineSource.read(reader); + if (!modifiedOutlineSource) { return { origViewZones: [], modViewZones: [] }; } + const origViewZones: IViewZone[] = []; const modViewZones: IViewZone[] = []; const sideBySide = this._options.renderSideBySide.read(reader); - const modifiedOutlineSource = this._modifiedOutlineSource.read(reader); - if (!modifiedOutlineSource) { return { origViewZones, modViewZones }; } - const curUnchangedRegions = unchangedRegions.read(reader); for (const r of curUnchangedRegions) { if (r.shouldHideControls(reader)) { @@ -94,13 +91,30 @@ export class UnchangedRangesFeature extends Disposable { const d = derived(reader => /** @description hiddenOriginalRangeStart */ r.getHiddenOriginalRange(reader).startLineNumber - 1); const origVz = new PlaceholderViewZone(d, 24); origViewZones.push(origVz); - store.add(new CollapsedCodeOverlayWidget(this._editors.original, origVz, r, r.originalRange, !sideBySide, modifiedOutlineSource, l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined), this._options)); + store.add(new CollapsedCodeOverlayWidget( + this._editors.original, + origVz, + r, + r.originalUnchangedRange, + !sideBySide, + modifiedOutlineSource, + l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined), + this._options + )); } { const d = derived(reader => /** @description hiddenModifiedRangeStart */ r.getHiddenModifiedRange(reader).startLineNumber - 1); const modViewZone = new PlaceholderViewZone(d, 24); modViewZones.push(modViewZone); - store.add(new CollapsedCodeOverlayWidget(this._editors.modified, modViewZone, r, r.modifiedRange, false, modifiedOutlineSource, l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined), this._options)); + store.add(new CollapsedCodeOverlayWidget( + this._editors.modified, + modViewZone, + r, + r.modifiedUnchangedRange, + false, + modifiedOutlineSource, + l => this._diffModel.get()!.ensureModifiedLineIsVisible(l, undefined), this._options + )); } } @@ -125,14 +139,14 @@ export class UnchangedRangesFeature extends Disposable { /** @description decorations */ const curUnchangedRegions = unchangedRegions.read(reader); const result = curUnchangedRegions.map(r => ({ - range: r.originalRange.toInclusiveRange()!, + range: r.originalUnchangedRange.toInclusiveRange()!, options: unchangedLinesDecoration, })); for (const r of curUnchangedRegions) { if (r.shouldHideControls(reader)) { result.push({ range: Range.fromPositions(new Position(r.originalLineNumber, 1)), - options: unchangedLinesDecorationShow + options: unchangedLinesDecorationShow, }); } } @@ -143,14 +157,14 @@ export class UnchangedRangesFeature extends Disposable { /** @description decorations */ const curUnchangedRegions = unchangedRegions.read(reader); const result = curUnchangedRegions.map(r => ({ - range: r.modifiedRange.toInclusiveRange()!, + range: r.modifiedUnchangedRange.toInclusiveRange()!, options: unchangedLinesDecoration, })); for (const r of curUnchangedRegions) { if (r.shouldHideControls(reader)) { result.push({ range: LineRange.ofLength(r.modifiedLineNumber, 1).toInclusiveRange()!, - options: unchangedLinesDecorationShow + options: unchangedLinesDecorationShow, }); } } @@ -172,7 +186,7 @@ export class UnchangedRangesFeature extends Disposable { const lineNumber = event.target.position.lineNumber; const model = this._diffModel.get(); if (!model) { return; } - const region = model.unchangedRegions.get().find(r => r.modifiedRange.includes(lineNumber)); + const region = model.unchangedRegions.get().find(r => r.modifiedUnchangedRange.includes(lineNumber)); if (!region) { return; } region.collapseAll(undefined); event.event.stopPropagation(); @@ -185,7 +199,7 @@ export class UnchangedRangesFeature extends Disposable { const lineNumber = event.target.position.lineNumber; const model = this._diffModel.get(); if (!model) { return; } - const region = model.unchangedRegions.get().find(r => r.originalRange.includes(lineNumber)); + const region = model.unchangedRegions.get().find(r => r.originalUnchangedRange.includes(lineNumber)); if (!region) { return; } region.collapseAll(undefined); event.event.stopPropagation(); @@ -195,12 +209,6 @@ export class UnchangedRangesFeature extends Disposable { } } -class DisposableCancellationTokenSource extends CancellationTokenSource { - public override dispose() { - super.dispose(true); - } -} - class OutlineSource extends Disposable { private readonly _currentModel = observableValue('current model', undefined); @@ -251,7 +259,8 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { h('div.top@top', { title: localize('diff.hiddenLines.top', 'Click or drag to show more above') }), h('div.center@content', { style: { display: 'flex' } }, [ h('div@first', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center', flexShrink: '0' } }, - [$('a', { title: localize('showAll', 'Show all'), role: 'button', onclick: () => { this.showAll(); } }, ...renderLabelWithIcons('$(unfold)'))] + [$('a', { title: localize('showAll', 'Show all'), role: 'button', onclick: () => { this._unchangedRegion.showAll(undefined); } }, + ...renderLabelWithIcons('$(unfold)'))] ), h('div@others', { style: { display: 'flex', justifyContent: 'center', alignItems: 'center' } }), ]), @@ -370,7 +379,7 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { span.addEventListener('dblclick', e => { if (e.button !== 0) { return; } e.preventDefault(); - this.showAll(); + this._unchangedRegion.showAll(undefined); }); children.push(span); @@ -405,6 +414,4 @@ class CollapsedCodeOverlayWidget extends ViewZoneOverlayWidget { reset(this._nodes.others, ...children); })); } - - private showAll() { this._unchangedRegion.showAll(undefined); } } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts index e2326b84447..407fbbbb84c 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDimension } from 'vs/base/browser/dom'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isHotReloadEnabled, registerHotReloadHandler } from 'vs/base/common/hotReload'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; @@ -369,3 +370,9 @@ export function applyViewZones(editor: ICodeEditor, viewZones: IObservable { suite('UnchangedRegion', () => { function serialize(regions: UnchangedRegion[]): unknown { - return regions.map(r => `${r.originalRange} - ${r.modifiedRange}`); + return regions.map(r => `${r.originalUnchangedRange} - ${r.modifiedUnchangedRange}`); } test('Everything changed', () => { From 2fa00ad43eeb5f8b815e0704dc238c6d86e85e5c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Sat, 2 Sep 2023 17:40:25 +0200 Subject: [PATCH 459/607] Fixes #191983 - Moved Code Should Scan Previous/Next Lines --- src/vs/base/common/arrays.ts | 6 ++ src/vs/base/common/arraysFind.ts | 8 +- .../diffEditorWidget2/accessibleDiffViewer.ts | 33 +------ src/vs/editor/common/core/lineRange.ts | 5 + src/vs/editor/common/core/offsetRange.ts | 6 ++ .../common/diff/advancedLinesDiffComputer.ts | 99 +++++++++++++++++++ .../common/diff/algorithms/diffAlgorithm.ts | 18 ++++ 7 files changed, 141 insertions(+), 34 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 71bedc3a440..4061404c8ee 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -201,6 +201,12 @@ export function* groupAdjacentBy(items: Iterable, shouldBeGrouped: (item1: } } +export function forEachAdjacent(arr: T[], f: (item1: T | undefined, item2: T | undefined) => void): void { + for (let i = 0; i <= arr.length; i++) { + f(i === 0 ? undefined : arr[i - 1], i === arr.length ? undefined : arr[i]); + } +} + interface IMutableSplice extends ISplice { readonly toInsert: T[]; deleteCount: number; diff --git a/src/vs/base/common/arraysFind.ts b/src/vs/base/common/arraysFind.ts index 91a1b710823..d40aba8ab67 100644 --- a/src/vs/base/common/arraysFind.ts +++ b/src/vs/base/common/arraysFind.ts @@ -81,7 +81,7 @@ export class MonotonousArray { public static assertInvariants = false; private _findLastMonotonousLastIdx = 0; - private _lastPredicate: ((item: T) => boolean) | undefined; + private _prevFindLastPredicate: ((item: T) => boolean) | undefined; constructor(private readonly _items: T[]) { } @@ -92,14 +92,14 @@ export class MonotonousArray { */ findLastMonotonous(predicate: (item: T) => boolean): T | undefined { if (MonotonousArray.assertInvariants) { - if (this._lastPredicate) { + if (this._prevFindLastPredicate) { for (const item of this._items) { - if (this._lastPredicate(item) && !predicate(item)) { + if (this._prevFindLastPredicate(item) && !predicate(item)) { throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.'); } } } - this._lastPredicate = predicate; + this._prevFindLastPredicate = predicate; } const idx = findLastIdxMonotonous(this._items, predicate, this._findLastMonotonousLastIdx); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts index ce188250447..5bb6c97f1e4 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts @@ -7,6 +7,7 @@ import { addDisposableListener, addStandardDisposableListener, reset } from 'vs/ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Action } from 'vs/base/common/actions'; +import { forEachAdjacent, groupAdjacentBy } from 'vs/base/common/arrays'; import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; @@ -224,7 +225,7 @@ const viewElementGroupLineMargin = 3; function computeViewElementGroups(diffs: DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number): ViewElementGroup[] { const result: ViewElementGroup[] = []; - for (const g of group(diffs, (a, b) => (b.modified.startLineNumber - a.modified.endLineNumberExclusive < 2 * viewElementGroupLineMargin))) { + for (const g of groupAdjacentBy(diffs, (a, b) => (b.modified.startLineNumber - a.modified.endLineNumberExclusive < 2 * viewElementGroupLineMargin))) { const viewElements: ViewElement[] = []; viewElements.push(new HeaderViewElement()); @@ -237,7 +238,7 @@ function computeViewElementGroups(diffs: DetailedLineRangeMapping[], originalLin Math.min(g[g.length - 1].modified.endLineNumberExclusive + viewElementGroupLineMargin, modifiedLineCount + 1) ); - forEachAdjacentItems(g, (a, b) => { + forEachAdjacent(g, (a, b) => { const origRange = new LineRange(a ? a.original.endLineNumberExclusive : origFullRange.startLineNumber, b ? b.original.startLineNumber : origFullRange.endLineNumberExclusive); const modifiedRange = new LineRange(a ? a.modified.endLineNumberExclusive : modifiedFullRange.startLineNumber, b ? b.modified.startLineNumber : modifiedFullRange.endLineNumberExclusive); @@ -659,31 +660,3 @@ class View extends Disposable { return r.html; } } - -function forEachAdjacentItems(items: T[], callback: (item1: T | undefined, item2: T | undefined) => void) { - let last: T | undefined; - for (const item of items) { - callback(last, item); - last = item; - } - callback(last, undefined); -} - -function* group(items: Iterable, shouldBeGrouped: (item1: T, item2: T) => boolean): Iterable { - let currentGroup: T[] | undefined; - let last: T | undefined; - for (const item of items) { - if (last !== undefined && shouldBeGrouped(last, item)) { - currentGroup!.push(item); - } else { - if (currentGroup) { - yield currentGroup; - } - currentGroup = [item]; - } - last = item; - } - if (currentGroup) { - yield currentGroup; - } -} diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index 3bf932cac4d..6a99f241df0 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -240,6 +240,11 @@ export class LineRangeSet { } } + contains(lineNumber: number): boolean { + const rangeThatStartsBeforeEnd = findLastMonotonous(this._normalizedRanges, r => r.startLineNumber <= lineNumber); + return !!rangeThatStartsBeforeEnd && rangeThatStartsBeforeEnd.endLineNumberExclusive > lineNumber; + } + intersects(range: LineRange): boolean { const rangeThatStartsBeforeEnd = findLastMonotonous(this._normalizedRanges, r => r.startLineNumber < range.endLineNumberExclusive); return !!rangeThatStartsBeforeEnd && rangeThatStartsBeforeEnd.endLineNumberExclusive > range.startLineNumber; diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index 9173e5339bb..64c29063206 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -144,6 +144,12 @@ export class OffsetRange { } return result; } + + public forEach(f: (offset: number) => void): void { + for (let i = this.start; i < this.endExclusive; i++) { + f(i); + } + } } export class OffsetRangeSet { diff --git a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts index 8720d8524ba..c5d597137f1 100644 --- a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts @@ -195,6 +195,8 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { changes.filter(c => !excludedChanges.has(c)), hashedOriginalLines, hashedModifiedLines, + originalLines, + modifiedLines, timeout ); pushMany(moves, unchangedMoves); @@ -297,6 +299,8 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { changes: DetailedLineRangeMapping[], hashedOriginalLines: number[], hashedModifiedLines: number[], + originalLines: string[], + modifiedLines: string[], timeout: ITimeout, ) { const moves: LineRangeMapping[] = []; @@ -380,9 +384,104 @@ export class AdvancedLinesDiffComputer implements ILinesDiffComputer { } } + moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); + + const monotonousChanges = new MonotonousArray(changes); + for (let i = 0; i < moves.length; i++) { + const move = moves[i]; + const firstTouchingChangeOrig = monotonousChanges.findLastMonotonous(c => c.original.startLineNumber <= move.original.startLineNumber)!; + const firstTouchingChangeMod = findLastMonotonous(changes, c => c.modified.startLineNumber <= move.modified.startLineNumber)!; + const linesAbove = Math.max( + move.original.startLineNumber - firstTouchingChangeOrig.original.startLineNumber, + move.modified.startLineNumber - firstTouchingChangeMod.modified.startLineNumber + ); + + const lastTouchingChangeOrig = monotonousChanges.findLastMonotonous(c => c.original.startLineNumber < move.original.endLineNumberExclusive)!; + const lastTouchingChangeMod = findLastMonotonous(changes, c => c.modified.startLineNumber < move.modified.endLineNumberExclusive)!; + const linesBelow = Math.max( + lastTouchingChangeOrig.original.endLineNumberExclusive - move.original.endLineNumberExclusive, + lastTouchingChangeMod.modified.endLineNumberExclusive - move.modified.endLineNumberExclusive + ); + + let extendToTop: number; + for (extendToTop = 0; extendToTop < linesAbove; extendToTop++) { + const origLine = move.original.startLineNumber - extendToTop - 1; + const modLine = move.modified.startLineNumber - extendToTop - 1; + if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) { + break; + } + if (!this.areLinesSimilar(originalLines[origLine - 1], modifiedLines[modLine - 1], timeout)) { + break; + } + } + + if (extendToTop > 0) { + originalSet.addRange(new LineRange(move.original.startLineNumber - extendToTop, move.original.startLineNumber)); + modifiedSet.addRange(new LineRange(move.modified.startLineNumber - extendToTop, move.modified.startLineNumber)); + } + + let extendToBottom: number; + for (extendToBottom = 0; extendToBottom < linesBelow; extendToBottom++) { + const origLine = move.original.endLineNumberExclusive + extendToBottom; + const modLine = move.modified.endLineNumberExclusive + extendToBottom; + if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) { + break; + } + if (!this.areLinesSimilar(originalLines[origLine - 1], modifiedLines[modLine - 1], timeout)) { + break; + } + } + + if (extendToBottom > 0) { + originalSet.addRange(new LineRange(move.original.endLineNumberExclusive, move.original.endLineNumberExclusive + extendToBottom)); + modifiedSet.addRange(new LineRange(move.modified.endLineNumberExclusive, move.modified.endLineNumberExclusive + extendToBottom)); + } + + if (extendToTop > 0 || extendToBottom > 0) { + moves[i] = new LineRangeMapping( + new LineRange(move.original.startLineNumber - extendToTop, move.original.endLineNumberExclusive + extendToBottom), + new LineRange(move.modified.startLineNumber - extendToTop, move.modified.endLineNumberExclusive + extendToBottom), + ); + } + } + return moves; } + private areLinesSimilar(line1: string, line2: string, timeout: ITimeout): boolean { + if (line1.trim() === line2.trim()) { return true; } + if (line1.length > 300 && line2.length > 300) { return false; } + + const result = this.myersDiffingAlgorithm.compute( + new LinesSliceCharSequence([line1], new OffsetRange(0, 1), false), + new LinesSliceCharSequence([line2], new OffsetRange(0, 1), false), + timeout + ); + let commonNonSpaceCharCount = 0; + const inverted = SequenceDiff.invert(result.diffs, line1.length); + for (const seq of inverted) { + seq.seq1Range.forEach(idx => { + if (!isSpace(line1.charCodeAt(idx))) { + commonNonSpaceCharCount++; + } + }); + } + + function countNonWsChars(str: string): number { + let count = 0; + for (let i = 0; i < line1.length; i++) { + if (!isSpace(str.charCodeAt(i))) { + count++; + } + } + return count; + } + + const longerLineLength = countNonWsChars(line1.length > line2.length ? line1 : line2); + const r = commonNonSpaceCharCount / longerLineLength > 0.6 && longerLineLength > 10; + return r; + } + private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } { const slice1 = new LinesSliceCharSequence(originalLines, diff.seq1Range, considerWhitespaceChanges); const slice2 = new LinesSliceCharSequence(modifiedLines, diff.seq2Range, considerWhitespaceChanges); diff --git a/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts b/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts index ce23a58ceb3..c38a84c2845 100644 --- a/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts +++ b/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { forEachAdjacent } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; @@ -33,6 +34,23 @@ export class DiffAlgorithmResult { } export class SequenceDiff { + public static invert(sequenceDiffs: SequenceDiff[], doc1Length: number): SequenceDiff[] { + const result: SequenceDiff[] = []; + + forEachAdjacent(sequenceDiffs, (a, b) => { + const seq1Start = a ? a.seq1Range.endExclusive : 0; + const seq2Start = a ? a.seq2Range.endExclusive : 0; + const seq1EndEx = b ? b.seq1Range.start : doc1Length; + const seq2EndEx = b ? b.seq2Range.start : (a ? a.seq2Range.endExclusive - a.seq1Range.endExclusive : 0) + doc1Length; + result.push(new SequenceDiff( + new OffsetRange(seq1Start, seq1EndEx), + new OffsetRange(seq2Start, seq2EndEx), + )); + }); + + return result; + } + constructor( public readonly seq1Range: OffsetRange, public readonly seq2Range: OffsetRange, From e80db1ae0acf5c79eb39905e34fee7a4460001be Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 09:42:52 +0200 Subject: [PATCH 460/607] Refactors diff algorithm --- .../diffEditorWidget2/diffEditorViewModel.ts | 4 +- .../common/diff/advancedLinesDiffComputer.ts | 971 ------------------ src/vs/editor/common/diff/algorithms/utils.ts | 20 - .../algorithms/diffAlgorithm.ts | 2 +- .../algorithms/dynamicProgrammingDiffing.ts | 4 +- .../algorithms/myersDiffAlgorithm.ts | 2 +- .../defaultLinesDiffComputer/computeMoves.ts | 314 ++++++ .../defaultLinesDiffComputer.ts | 310 ++++++ .../heuristicSequenceOptimizations.ts} | 429 +++++--- .../defaultLinesDiffComputer/lineSequence.ts | 45 + .../linesSliceCharSequence.ts | 217 ++++ .../diff/defaultLinesDiffComputer/utils.ts | 74 ++ .../editor/common/diff/linesDiffComputers.ts | 7 +- src/vs/editor/common/diff/rangeMapping.ts | 5 +- .../common/services/editorSimpleWorker.ts | 4 +- .../diffing/advancedLinesDiffComputer.test.ts | 2 +- .../test/node/diffing/diffingFixture.test.ts | 4 +- src/vs/monaco.d.ts | 4 + 18 files changed, 1245 insertions(+), 1173 deletions(-) delete mode 100644 src/vs/editor/common/diff/advancedLinesDiffComputer.ts delete mode 100644 src/vs/editor/common/diff/algorithms/utils.ts rename src/vs/editor/common/diff/{ => defaultLinesDiffComputer}/algorithms/diffAlgorithm.ts (99%) rename src/vs/editor/common/diff/{ => defaultLinesDiffComputer}/algorithms/dynamicProgrammingDiffing.ts (95%) rename src/vs/editor/common/diff/{ => defaultLinesDiffComputer}/algorithms/myersDiffAlgorithm.ts (97%) create mode 100644 src/vs/editor/common/diff/defaultLinesDiffComputer/computeMoves.ts create mode 100644 src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts rename src/vs/editor/common/diff/{algorithms/joinSequenceDiffs.ts => defaultLinesDiffComputer/heuristicSequenceOptimizations.ts} (72%) create mode 100644 src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts create mode 100644 src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts create mode 100644 src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index dd4714d7173..30697f69a85 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -9,7 +9,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; -import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; @@ -175,7 +175,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo debouncer.cancel(); contentChangedSignal.read(reader); documentDiffProviderOptionChanged.read(reader); - readHotReloadableExport(AdvancedLinesDiffComputer, reader); + readHotReloadableExport(DefaultLinesDiffComputer, reader); this._isDiffUpToDate.set(false, undefined); diff --git a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts b/src/vs/editor/common/diff/advancedLinesDiffComputer.ts deleted file mode 100644 index c5d597137f1..00000000000 --- a/src/vs/editor/common/diff/advancedLinesDiffComputer.ts +++ /dev/null @@ -1,971 +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 { compareBy, equals, groupAdjacentBy, numberComparator, pushMany, reverseOrder } from 'vs/base/common/arrays'; -import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; -import { CharCode } from 'vs/base/common/charCode'; -import { SetMap } from 'vs/base/common/collections'; -import { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; -import { OffsetRange } from 'vs/editor/common/core/offsetRange'; -import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; -import { DateTimeout, ISequence, ITimeout, InfiniteTimeout, SequenceDiff } from 'vs/editor/common/diff/algorithms/diffAlgorithm'; -import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/algorithms/dynamicProgrammingDiffing'; -import { optimizeSequenceDiffs, removeRandomLineMatches, removeRandomMatches, smoothenSequenceDiffs } from 'vs/editor/common/diff/algorithms/joinSequenceDiffs'; -import { MyersDiffAlgorithm } from 'vs/editor/common/diff/algorithms/myersDiffAlgorithm'; -import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; -import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from './rangeMapping'; -import { MonotonousArray, findLastIdxMonotonous, findLastMonotonous, findFirstMonotonous } from 'vs/base/common/arraysFind'; - -export class AdvancedLinesDiffComputer implements ILinesDiffComputer { - private readonly dynamicProgrammingDiffing = new DynamicProgrammingDiffing(); - private readonly myersDiffingAlgorithm = new MyersDiffAlgorithm(); - - computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): LinesDiff { - if (originalLines.length <= 1 && equals(originalLines, modifiedLines, (a, b) => a === b)) { - return new LinesDiff([], [], false); - } - - if (originalLines.length === 1 && originalLines[0].length === 0 || modifiedLines.length === 1 && modifiedLines[0].length === 0) { - return new LinesDiff([ - new DetailedLineRangeMapping( - new LineRange(1, originalLines.length + 1), - new LineRange(1, modifiedLines.length + 1), - [ - new RangeMapping( - new Range(1, 1, originalLines.length, originalLines[0].length + 1), - new Range(1, 1, modifiedLines.length, modifiedLines[0].length + 1) - ) - ] - ) - ], [], false); - } - - const timeout = options.maxComputationTimeMs === 0 ? InfiniteTimeout.instance : new DateTimeout(options.maxComputationTimeMs); - const considerWhitespaceChanges = !options.ignoreTrimWhitespace; - - const perfectHashes = new Map(); - function getOrCreateHash(text: string): number { - let hash = perfectHashes.get(text); - if (hash === undefined) { - hash = perfectHashes.size; - perfectHashes.set(text, hash); - } - return hash; - } - - const srcDocLines = originalLines.map((l) => getOrCreateHash(l.trim())); - const tgtDocLines = modifiedLines.map((l) => getOrCreateHash(l.trim())); - - const sequence1 = new LineSequence(srcDocLines, originalLines); - const sequence2 = new LineSequence(tgtDocLines, modifiedLines); - - const lineAlignmentResult = (() => { - if (sequence1.length + sequence2.length < 1700) { - // Use the improved algorithm for small files - return this.dynamicProgrammingDiffing.compute( - sequence1, - sequence2, - timeout, - (offset1, offset2) => - originalLines[offset1] === modifiedLines[offset2] - ? modifiedLines[offset2].length === 0 - ? 0.1 - : 1 + Math.log(1 + modifiedLines[offset2].length) - : 0.99 - ); - } - - return this.myersDiffingAlgorithm.compute( - sequence1, - sequence2 - ); - })(); - - let lineAlignments = lineAlignmentResult.diffs; - let hitTimeout = lineAlignmentResult.hitTimeout; - lineAlignments = optimizeSequenceDiffs(sequence1, sequence2, lineAlignments); - lineAlignments = removeRandomLineMatches(sequence1, sequence2, lineAlignments); - - const alignments: RangeMapping[] = []; - - const scanForWhitespaceChanges = (equalLinesCount: number) => { - if (!considerWhitespaceChanges) { - return; - } - - for (let i = 0; i < equalLinesCount; i++) { - const seq1Offset = seq1LastStart + i; - const seq2Offset = seq2LastStart + i; - if (originalLines[seq1Offset] !== modifiedLines[seq2Offset]) { - // This is because of whitespace changes, diff these lines - const characterDiffs = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( - new OffsetRange(seq1Offset, seq1Offset + 1), - new OffsetRange(seq2Offset, seq2Offset + 1), - ), timeout, considerWhitespaceChanges); - for (const a of characterDiffs.mappings) { - alignments.push(a); - } - if (characterDiffs.hitTimeout) { - hitTimeout = true; - } - } - } - }; - - let seq1LastStart = 0; - let seq2LastStart = 0; - - for (const diff of lineAlignments) { - assertFn(() => diff.seq1Range.start - seq1LastStart === diff.seq2Range.start - seq2LastStart); - - const equalLinesCount = diff.seq1Range.start - seq1LastStart; - - scanForWhitespaceChanges(equalLinesCount); - - seq1LastStart = diff.seq1Range.endExclusive; - seq2LastStart = diff.seq2Range.endExclusive; - - const characterDiffs = this.refineDiff(originalLines, modifiedLines, diff, timeout, considerWhitespaceChanges); - if (characterDiffs.hitTimeout) { - hitTimeout = true; - } - for (const a of characterDiffs.mappings) { - alignments.push(a); - } - } - - scanForWhitespaceChanges(originalLines.length - seq1LastStart); - - const changes = lineRangeMappingFromRangeMappings(alignments, originalLines, modifiedLines); - - let moves: MovedText[] = []; - if (options.computeMoves) { - moves = this.computeMoves(changes, originalLines, modifiedLines, srcDocLines, tgtDocLines, timeout, considerWhitespaceChanges); - } - - // Make sure all ranges are valid - assertFn(() => { - function validatePosition(pos: Position, lines: string[]): boolean { - if (pos.lineNumber < 1 || pos.lineNumber > lines.length) { return false; } - const line = lines[pos.lineNumber - 1]; - if (pos.column < 1 || pos.column > line.length + 1) { return false; } - return true; - } - - function validateRange(range: LineRange, lines: string[]): boolean { - if (range.startLineNumber < 1 || range.startLineNumber > lines.length + 1) { return false; } - if (range.endLineNumberExclusive < 1 || range.endLineNumberExclusive > lines.length + 1) { return false; } - return true; - } - - for (const c of changes) { - if (!c.innerChanges) { return false; } - for (const ic of c.innerChanges) { - const valid = validatePosition(ic.modifiedRange.getStartPosition(), modifiedLines) && validatePosition(ic.modifiedRange.getEndPosition(), modifiedLines) && - validatePosition(ic.originalRange.getStartPosition(), originalLines) && validatePosition(ic.originalRange.getEndPosition(), originalLines); - if (!valid) { return false; } - } - if (!validateRange(c.modified, modifiedLines) || !validateRange(c.original, originalLines)) { - return false; - } - } - return true; - }); - - return new LinesDiff(changes, moves, hitTimeout); - } - - private computeMoves( - changes: DetailedLineRangeMapping[], - originalLines: string[], - modifiedLines: string[], - hashedOriginalLines: number[], - hashedModifiedLines: number[], - timeout: ITimeout, - considerWhitespaceChanges: boolean, - ): MovedText[] { - const { moves, excludedChanges } = this.computeMovesFromSimpleDeletionsToSimpleInsertions(changes, originalLines, modifiedLines, timeout); - - if (!timeout.isValid()) { return []; } - - const unchangedMoves = this.computeUnchangedMoves( - changes.filter(c => !excludedChanges.has(c)), - hashedOriginalLines, - hashedModifiedLines, - originalLines, - modifiedLines, - timeout - ); - pushMany(moves, unchangedMoves); - - // join moves - moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); - if (moves.length === 0) { - return []; - } - let joinedMoves = [moves[0]]; - for (let i = 1; i < moves.length; i++) { - const last = joinedMoves[joinedMoves.length - 1]; - const current = moves[i]; - - const originalDist = current.original.startLineNumber - last.original.endLineNumberExclusive; - const modifiedDist = current.modified.startLineNumber - last.modified.endLineNumberExclusive; - const currentMoveAfterLast = originalDist >= 0 && modifiedDist >= 0; - - if (currentMoveAfterLast && originalDist + modifiedDist <= 2) { - joinedMoves[joinedMoves.length - 1] = last.join(current); - continue; - } - - const originalText = current.original.toOffsetRange().slice(originalLines).map(l => l.trim()).join('\n'); - if (originalText.length <= 10) { - // Ignore small moves - continue; - } - joinedMoves.push(current); - } - - // Ignore non moves - const changesMonotonous = new MonotonousArray(changes); - joinedMoves = joinedMoves.filter(m => { - const diffBeforeOriginalMove = changesMonotonous.findLastMonotonous(c => c.original.endLineNumberExclusive <= m.original.startLineNumber) - || new LineRangeMapping(new LineRange(1, 1), new LineRange(1, 1)); - - const modifiedDistToPrevDiff = m.modified.startLineNumber - diffBeforeOriginalMove.modified.endLineNumberExclusive; - const originalDistToPrevDiff = m.original.startLineNumber - diffBeforeOriginalMove.original.endLineNumberExclusive; - - const differentDistances = modifiedDistToPrevDiff !== originalDistToPrevDiff; - return differentDistances; - }); - - const movesWithDiffs = joinedMoves.map(m => { - const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( - m.original.toOffsetRange(), - m.modified.toOffsetRange(), - ), timeout, considerWhitespaceChanges); - const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); - return new MovedText(m, mappings); - }); - return movesWithDiffs; - } - - private computeMovesFromSimpleDeletionsToSimpleInsertions( - changes: DetailedLineRangeMapping[], - originalLines: string[], - modifiedLines: string[], - timeout: ITimeout, - ) { - const moves: LineRangeMapping[] = []; - - const deletions = changes - .filter(c => c.modified.isEmpty && c.original.length >= 3) - .map(d => new LineRangeFragment(d.original, originalLines, d)); - const insertions = new Set(changes - .filter(c => c.original.isEmpty && c.modified.length >= 3) - .map(d => new LineRangeFragment(d.modified, modifiedLines, d))); - - const excludedChanges = new Set(); - - for (const deletion of deletions) { - let highestSimilarity = -1; - let best: LineRangeFragment | undefined; - for (const insertion of insertions) { - const similarity = deletion.computeSimilarity(insertion); - if (similarity > highestSimilarity) { - highestSimilarity = similarity; - best = insertion; - } - } - - if (highestSimilarity > 0.90 && best) { - insertions.delete(best); - moves.push(new LineRangeMapping(deletion.range, best.range)); - excludedChanges.add(deletion.source); - excludedChanges.add(best.source); - } - - if (!timeout.isValid()) { - return { moves, excludedChanges }; - } - } - - return { moves, excludedChanges }; - } - - private computeUnchangedMoves( - changes: DetailedLineRangeMapping[], - hashedOriginalLines: number[], - hashedModifiedLines: number[], - originalLines: string[], - modifiedLines: string[], - timeout: ITimeout, - ) { - const moves: LineRangeMapping[] = []; - - const original3LineHashes = new SetMap(); - - for (const change of changes) { - for (let i = change.original.startLineNumber; i < change.original.endLineNumberExclusive - 2; i++) { - const key = `${hashedOriginalLines[i - 1]}:${hashedOriginalLines[i + 1 - 1]}:${hashedOriginalLines[i + 2 - 1]}`; - original3LineHashes.add(key, { range: new LineRange(i, i + 3) }); - } - } - - interface PossibleMapping { - modifiedLineRange: LineRange; - originalLineRange: LineRange; - } - - const possibleMappings: PossibleMapping[] = []; - - changes.sort(compareBy(c => c.modified.startLineNumber, numberComparator)); - - for (const change of changes) { - let lastMappings: PossibleMapping[] = []; - for (let i = change.modified.startLineNumber; i < change.modified.endLineNumberExclusive - 2; i++) { - const key = `${hashedModifiedLines[i - 1]}:${hashedModifiedLines[i + 1 - 1]}:${hashedModifiedLines[i + 2 - 1]}`; - const currentModifiedRange = new LineRange(i, i + 3); - - const nextMappings: PossibleMapping[] = []; - original3LineHashes.forEach(key, ({ range }) => { - for (const lastMapping of lastMappings) { - // does this match extend some last match? - if (lastMapping.originalLineRange.endLineNumberExclusive + 1 === range.endLineNumberExclusive && - lastMapping.modifiedLineRange.endLineNumberExclusive + 1 === currentModifiedRange.endLineNumberExclusive) { - lastMapping.originalLineRange = new LineRange(lastMapping.originalLineRange.startLineNumber, range.endLineNumberExclusive); - lastMapping.modifiedLineRange = new LineRange(lastMapping.modifiedLineRange.startLineNumber, currentModifiedRange.endLineNumberExclusive); - nextMappings.push(lastMapping); - return; - } - } - - const mapping: PossibleMapping = { - modifiedLineRange: currentModifiedRange, - originalLineRange: range, - }; - possibleMappings.push(mapping); - nextMappings.push(mapping); - }); - lastMappings = nextMappings; - } - - if (!timeout.isValid()) { - return []; - } - } - - possibleMappings.sort(reverseOrder(compareBy(m => m.modifiedLineRange.length, numberComparator))); - - const modifiedSet = new LineRangeSet(); - const originalSet = new LineRangeSet(); - - for (const mapping of possibleMappings) { - - const diffOrigToMod = mapping.modifiedLineRange.startLineNumber - mapping.originalLineRange.startLineNumber; - const modifiedSections = modifiedSet.subtractFrom(mapping.modifiedLineRange); - const originalTranslatedSections = originalSet.subtractFrom(mapping.originalLineRange).getWithDelta(diffOrigToMod); - - const modifiedIntersectedSections = modifiedSections.getIntersection(originalTranslatedSections); - - for (const s of modifiedIntersectedSections.ranges) { - if (s.length < 3) { - continue; - } - const modifiedLineRange = s; - const originalLineRange = s.delta(-diffOrigToMod); - - moves.push(new LineRangeMapping(originalLineRange, modifiedLineRange)); - - modifiedSet.addRange(modifiedLineRange); - originalSet.addRange(originalLineRange); - } - } - - moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); - - const monotonousChanges = new MonotonousArray(changes); - for (let i = 0; i < moves.length; i++) { - const move = moves[i]; - const firstTouchingChangeOrig = monotonousChanges.findLastMonotonous(c => c.original.startLineNumber <= move.original.startLineNumber)!; - const firstTouchingChangeMod = findLastMonotonous(changes, c => c.modified.startLineNumber <= move.modified.startLineNumber)!; - const linesAbove = Math.max( - move.original.startLineNumber - firstTouchingChangeOrig.original.startLineNumber, - move.modified.startLineNumber - firstTouchingChangeMod.modified.startLineNumber - ); - - const lastTouchingChangeOrig = monotonousChanges.findLastMonotonous(c => c.original.startLineNumber < move.original.endLineNumberExclusive)!; - const lastTouchingChangeMod = findLastMonotonous(changes, c => c.modified.startLineNumber < move.modified.endLineNumberExclusive)!; - const linesBelow = Math.max( - lastTouchingChangeOrig.original.endLineNumberExclusive - move.original.endLineNumberExclusive, - lastTouchingChangeMod.modified.endLineNumberExclusive - move.modified.endLineNumberExclusive - ); - - let extendToTop: number; - for (extendToTop = 0; extendToTop < linesAbove; extendToTop++) { - const origLine = move.original.startLineNumber - extendToTop - 1; - const modLine = move.modified.startLineNumber - extendToTop - 1; - if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) { - break; - } - if (!this.areLinesSimilar(originalLines[origLine - 1], modifiedLines[modLine - 1], timeout)) { - break; - } - } - - if (extendToTop > 0) { - originalSet.addRange(new LineRange(move.original.startLineNumber - extendToTop, move.original.startLineNumber)); - modifiedSet.addRange(new LineRange(move.modified.startLineNumber - extendToTop, move.modified.startLineNumber)); - } - - let extendToBottom: number; - for (extendToBottom = 0; extendToBottom < linesBelow; extendToBottom++) { - const origLine = move.original.endLineNumberExclusive + extendToBottom; - const modLine = move.modified.endLineNumberExclusive + extendToBottom; - if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) { - break; - } - if (!this.areLinesSimilar(originalLines[origLine - 1], modifiedLines[modLine - 1], timeout)) { - break; - } - } - - if (extendToBottom > 0) { - originalSet.addRange(new LineRange(move.original.endLineNumberExclusive, move.original.endLineNumberExclusive + extendToBottom)); - modifiedSet.addRange(new LineRange(move.modified.endLineNumberExclusive, move.modified.endLineNumberExclusive + extendToBottom)); - } - - if (extendToTop > 0 || extendToBottom > 0) { - moves[i] = new LineRangeMapping( - new LineRange(move.original.startLineNumber - extendToTop, move.original.endLineNumberExclusive + extendToBottom), - new LineRange(move.modified.startLineNumber - extendToTop, move.modified.endLineNumberExclusive + extendToBottom), - ); - } - } - - return moves; - } - - private areLinesSimilar(line1: string, line2: string, timeout: ITimeout): boolean { - if (line1.trim() === line2.trim()) { return true; } - if (line1.length > 300 && line2.length > 300) { return false; } - - const result = this.myersDiffingAlgorithm.compute( - new LinesSliceCharSequence([line1], new OffsetRange(0, 1), false), - new LinesSliceCharSequence([line2], new OffsetRange(0, 1), false), - timeout - ); - let commonNonSpaceCharCount = 0; - const inverted = SequenceDiff.invert(result.diffs, line1.length); - for (const seq of inverted) { - seq.seq1Range.forEach(idx => { - if (!isSpace(line1.charCodeAt(idx))) { - commonNonSpaceCharCount++; - } - }); - } - - function countNonWsChars(str: string): number { - let count = 0; - for (let i = 0; i < line1.length; i++) { - if (!isSpace(str.charCodeAt(i))) { - count++; - } - } - return count; - } - - const longerLineLength = countNonWsChars(line1.length > line2.length ? line1 : line2); - const r = commonNonSpaceCharCount / longerLineLength > 0.6 && longerLineLength > 10; - return r; - } - - private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } { - const slice1 = new LinesSliceCharSequence(originalLines, diff.seq1Range, considerWhitespaceChanges); - const slice2 = new LinesSliceCharSequence(modifiedLines, diff.seq2Range, considerWhitespaceChanges); - - const diffResult = slice1.length + slice2.length < 500 - ? this.dynamicProgrammingDiffing.compute(slice1, slice2, timeout) - : this.myersDiffingAlgorithm.compute(slice1, slice2, timeout); - - let diffs = diffResult.diffs; - diffs = optimizeSequenceDiffs(slice1, slice2, diffs); - diffs = coverFullWords(slice1, slice2, diffs); - diffs = smoothenSequenceDiffs(slice1, slice2, diffs); - diffs = removeRandomMatches(slice1, slice2, diffs); - - const result = diffs.map( - (d) => - new RangeMapping( - slice1.translateRange(d.seq1Range), - slice2.translateRange(d.seq2Range) - ) - ); - - // Assert: result applied on original should be the same as diff applied to original - - return { - mappings: result, - hitTimeout: diffResult.hitTimeout, - }; - } -} - -function coverFullWords(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { - const additional: SequenceDiff[] = []; - - let lastModifiedWord: { added: number; deleted: number; count: number; s1Range: OffsetRange; s2Range: OffsetRange } | undefined = undefined; - - function maybePushWordToAdditional() { - if (!lastModifiedWord) { - return; - } - - const originalLength1 = lastModifiedWord.s1Range.length - lastModifiedWord.deleted; - const originalLength2 = lastModifiedWord.s2Range.length - lastModifiedWord.added; - if (originalLength1 !== originalLength2) { - // TODO figure out why this happens - } - - if (Math.max(lastModifiedWord.deleted, lastModifiedWord.added) + (lastModifiedWord.count - 1) > originalLength1) { - additional.push(new SequenceDiff(lastModifiedWord.s1Range, lastModifiedWord.s2Range)); - } - - lastModifiedWord = undefined; - } - - for (const s of sequenceDiffs) { - function processWord(s1Range: OffsetRange, s2Range: OffsetRange) { - if (!lastModifiedWord || !lastModifiedWord.s1Range.containsRange(s1Range) || !lastModifiedWord.s2Range.containsRange(s2Range)) { - if (lastModifiedWord && !(lastModifiedWord.s1Range.endExclusive < s1Range.start && lastModifiedWord.s2Range.endExclusive < s2Range.start)) { - const s1Added = OffsetRange.tryCreate(lastModifiedWord.s1Range.endExclusive, s1Range.start); - const s2Added = OffsetRange.tryCreate(lastModifiedWord.s2Range.endExclusive, s2Range.start); - lastModifiedWord.deleted += s1Added?.length ?? 0; - lastModifiedWord.added += s2Added?.length ?? 0; - - lastModifiedWord.s1Range = lastModifiedWord.s1Range.join(s1Range); - lastModifiedWord.s2Range = lastModifiedWord.s2Range.join(s2Range); - } else { - maybePushWordToAdditional(); - lastModifiedWord = { added: 0, deleted: 0, count: 0, s1Range: s1Range, s2Range: s2Range }; - } - } - - const changedS1 = s1Range.intersect(s.seq1Range); - const changedS2 = s2Range.intersect(s.seq2Range); - lastModifiedWord.count++; - lastModifiedWord.deleted += changedS1?.length ?? 0; - lastModifiedWord.added += changedS2?.length ?? 0; - } - - const w1Before = sequence1.findWordContaining(s.seq1Range.start - 1); - const w2Before = sequence2.findWordContaining(s.seq2Range.start - 1); - - const w1After = sequence1.findWordContaining(s.seq1Range.endExclusive); - const w2After = sequence2.findWordContaining(s.seq2Range.endExclusive); - - if (w1Before && w1After && w2Before && w2After && w1Before.equals(w1After) && w2Before.equals(w2After)) { - processWord(w1Before, w2Before); - } else { - if (w1Before && w2Before) { - processWord(w1Before, w2Before); - } - if (w1After && w2After) { - processWord(w1After, w2After); - } - } - } - - maybePushWordToAdditional(); - - const merged = mergeSequenceDiffs(sequenceDiffs, additional); - return merged; -} - -function mergeSequenceDiffs(sequenceDiffs1: SequenceDiff[], sequenceDiffs2: SequenceDiff[]): SequenceDiff[] { - const result: SequenceDiff[] = []; - - while (sequenceDiffs1.length > 0 || sequenceDiffs2.length > 0) { - const sd1 = sequenceDiffs1[0]; - const sd2 = sequenceDiffs2[0]; - - let next: SequenceDiff; - if (sd1 && (!sd2 || sd1.seq1Range.start < sd2.seq1Range.start)) { - next = sequenceDiffs1.shift()!; - } else { - next = sequenceDiffs2.shift()!; - } - - if (result.length > 0 && result[result.length - 1].seq1Range.endExclusive >= next.seq1Range.start) { - result[result.length - 1] = result[result.length - 1].join(next); - } else { - result.push(next); - } - } - - return result; -} - -export function lineRangeMappingFromRangeMappings(alignments: RangeMapping[], originalLines: string[], modifiedLines: string[], dontAssertStartLine: boolean = false): DetailedLineRangeMapping[] { - const changes: DetailedLineRangeMapping[] = []; - for (const g of groupAdjacentBy( - alignments.map(a => getLineRangeMapping(a, originalLines, modifiedLines)), - (a1, a2) => - a1.original.overlapOrTouch(a2.original) - || a1.modified.overlapOrTouch(a2.modified) - )) { - const first = g[0]; - const last = g[g.length - 1]; - - changes.push(new DetailedLineRangeMapping( - first.original.join(last.original), - first.modified.join(last.modified), - g.map(a => a.innerChanges![0]), - )); - } - - assertFn(() => { - if (!dontAssertStartLine) { - if (changes.length > 0 && changes[0].original.startLineNumber !== changes[0].modified.startLineNumber) { - return false; - } - } - return checkAdjacentItems(changes, - (m1, m2) => m2.original.startLineNumber - m1.original.endLineNumberExclusive === m2.modified.startLineNumber - m1.modified.endLineNumberExclusive && - // There has to be an unchanged line in between (otherwise both diffs should have been joined) - m1.original.endLineNumberExclusive < m2.original.startLineNumber && - m1.modified.endLineNumberExclusive < m2.modified.startLineNumber, - ); - }); - - return changes; -} - -export function getLineRangeMapping(rangeMapping: RangeMapping, originalLines: string[], modifiedLines: string[]): DetailedLineRangeMapping { - let lineStartDelta = 0; - let lineEndDelta = 0; - - // rangeMapping describes the edit that replaces `rangeMapping.originalRange` with `newText := getText(modifiedLines, rangeMapping.modifiedRange)`. - - // original: ]xxx \n <- this line is not modified - // modified: ]xx \n - if (rangeMapping.modifiedRange.endColumn === 1 && rangeMapping.originalRange.endColumn === 1 - && rangeMapping.originalRange.startLineNumber + lineStartDelta <= rangeMapping.originalRange.endLineNumber - && rangeMapping.modifiedRange.startLineNumber + lineStartDelta <= rangeMapping.modifiedRange.endLineNumber) { - // We can only do this if the range is not empty yet - lineEndDelta = -1; - } - - // original: xxx[ \n <- this line is not modified - // modified: xxx[ \n - if (rangeMapping.modifiedRange.startColumn - 1 >= modifiedLines[rangeMapping.modifiedRange.startLineNumber - 1].length - && rangeMapping.originalRange.startColumn - 1 >= originalLines[rangeMapping.originalRange.startLineNumber - 1].length - && rangeMapping.originalRange.startLineNumber <= rangeMapping.originalRange.endLineNumber + lineEndDelta - && rangeMapping.modifiedRange.startLineNumber <= rangeMapping.modifiedRange.endLineNumber + lineEndDelta) { - // We can only do this if the range is not empty yet - lineStartDelta = 1; - } - - const originalLineRange = new LineRange( - rangeMapping.originalRange.startLineNumber + lineStartDelta, - rangeMapping.originalRange.endLineNumber + 1 + lineEndDelta - ); - const modifiedLineRange = new LineRange( - rangeMapping.modifiedRange.startLineNumber + lineStartDelta, - rangeMapping.modifiedRange.endLineNumber + 1 + lineEndDelta - ); - - return new DetailedLineRangeMapping(originalLineRange, modifiedLineRange, [rangeMapping]); -} - -export class LineSequence implements ISequence { - constructor( - private readonly trimmedHash: number[], - private readonly lines: string[] - ) { } - - getElement(offset: number): number { - return this.trimmedHash[offset]; - } - - get length(): number { - return this.trimmedHash.length; - } - - getBoundaryScore(length: number): number { - const indentationBefore = length === 0 ? 0 : getIndentation(this.lines[length - 1]); - const indentationAfter = length === this.lines.length ? 0 : getIndentation(this.lines[length]); - return 1000 - (indentationBefore + indentationAfter); - } - - getText(range: OffsetRange): string { - return this.lines.slice(range.start, range.endExclusive).join('\n'); - } - - isStronglyEqual(offset1: number, offset2: number): boolean { - return this.lines[offset1] === this.lines[offset2]; - } -} - -function getIndentation(str: string): number { - let i = 0; - while (i < str.length && (str.charCodeAt(i) === CharCode.Space || str.charCodeAt(i) === CharCode.Tab)) { - i++; - } - return i; -} - -export class LinesSliceCharSequence implements ISequence { - private readonly elements: number[] = []; - private readonly firstCharOffsetByLine: number[] = []; - public readonly lineRange: OffsetRange; - // To account for trimming - private readonly additionalOffsetByLine: number[] = []; - - constructor(public readonly lines: string[], lineRange: OffsetRange, public readonly considerWhitespaceChanges: boolean) { - // This slice has to have lineRange.length many \n! (otherwise diffing against an empty slice will be problematic) - // (Unless it covers the entire document, in that case the other slice also has to cover the entire document ands it's okay) - - // If the slice covers the end, but does not start at the beginning, we include just the \n of the previous line. - let trimFirstLineFully = false; - if (lineRange.start > 0 && lineRange.endExclusive >= lines.length) { - lineRange = new OffsetRange(lineRange.start - 1, lineRange.endExclusive); - trimFirstLineFully = true; - } - - this.lineRange = lineRange; - - this.firstCharOffsetByLine[0] = 0; - for (let i = this.lineRange.start; i < this.lineRange.endExclusive; i++) { - let line = lines[i]; - let offset = 0; - if (trimFirstLineFully) { - offset = line.length; - line = ''; - trimFirstLineFully = false; - } else if (!considerWhitespaceChanges) { - const trimmedStartLine = line.trimStart(); - offset = line.length - trimmedStartLine.length; - line = trimmedStartLine.trimEnd(); - } - - this.additionalOffsetByLine.push(offset); - - for (let i = 0; i < line.length; i++) { - this.elements.push(line.charCodeAt(i)); - } - - // Don't add an \n that does not exist in the document. - if (i < lines.length - 1) { - this.elements.push('\n'.charCodeAt(0)); - this.firstCharOffsetByLine[i - this.lineRange.start + 1] = this.elements.length; - } - } - // To account for the last line - this.additionalOffsetByLine.push(0); - } - - toString() { - return `Slice: "${this.text}"`; - } - - get text(): string { - return this.getText(new OffsetRange(0, this.length)); - } - - getText(range: OffsetRange): string { - return this.elements.slice(range.start, range.endExclusive).map(e => String.fromCharCode(e)).join(''); - } - - getElement(offset: number): number { - return this.elements[offset]; - } - - get length(): number { - return this.elements.length; - } - - public getBoundaryScore(length: number): number { - // a b c , d e f - // 11 0 0 12 15 6 13 0 0 11 - - const prevCategory = getCategory(length > 0 ? this.elements[length - 1] : -1); - const nextCategory = getCategory(length < this.elements.length ? this.elements[length] : -1); - - if (prevCategory === CharBoundaryCategory.LineBreakCR && nextCategory === CharBoundaryCategory.LineBreakLF) { - // don't break between \r and \n - return 0; - } - - let score = 0; - if (prevCategory !== nextCategory) { - score += 10; - if (nextCategory === CharBoundaryCategory.WordUpper) { - score += 1; - } - } - - score += getCategoryBoundaryScore(prevCategory); - score += getCategoryBoundaryScore(nextCategory); - - return score; - } - - public translateOffset(offset: number): Position { - // find smallest i, so that lineBreakOffsets[i] <= offset using binary search - if (this.lineRange.isEmpty) { - return new Position(this.lineRange.start + 1, 1); - } - - const i = findLastIdxMonotonous(this.firstCharOffsetByLine, (value) => value <= offset); - return new Position(this.lineRange.start + i + 1, offset - this.firstCharOffsetByLine[i] + this.additionalOffsetByLine[i] + 1); - } - - public translateRange(range: OffsetRange): Range { - return Range.fromPositions(this.translateOffset(range.start), this.translateOffset(range.endExclusive)); - } - - /** - * Finds the word that contains the character at the given offset - */ - public findWordContaining(offset: number): OffsetRange | undefined { - if (offset < 0 || offset >= this.elements.length) { - return undefined; - } - - if (!isWordChar(this.elements[offset])) { - return undefined; - } - - // find start - let start = offset; - while (start > 0 && isWordChar(this.elements[start - 1])) { - start--; - } - - // find end - let end = offset; - while (end < this.elements.length && isWordChar(this.elements[end])) { - end++; - } - - return new OffsetRange(start, end); - } - - public countLinesIn(range: OffsetRange): number { - return this.translateOffset(range.endExclusive).lineNumber - this.translateOffset(range.start).lineNumber; - } - - public isStronglyEqual(offset1: number, offset2: number): boolean { - return this.elements[offset1] === this.elements[offset2]; - } - - public extendToFullLines(range: OffsetRange): OffsetRange { - const start = findLastMonotonous(this.firstCharOffsetByLine, x => x <= range.start) ?? 0; - const end = findFirstMonotonous(this.firstCharOffsetByLine, x => range.endExclusive <= x) ?? this.elements.length; - return new OffsetRange(start, end); - } -} - -function isWordChar(charCode: number): boolean { - return charCode >= CharCode.a && charCode <= CharCode.z - || charCode >= CharCode.A && charCode <= CharCode.Z - || charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9; -} - -const enum CharBoundaryCategory { - WordLower, - WordUpper, - WordNumber, - End, - Other, - Space, - LineBreakCR, - LineBreakLF, -} - -const score: Record = { - [CharBoundaryCategory.WordLower]: 0, - [CharBoundaryCategory.WordUpper]: 0, - [CharBoundaryCategory.WordNumber]: 0, - [CharBoundaryCategory.End]: 10, - [CharBoundaryCategory.Other]: 2, - [CharBoundaryCategory.Space]: 3, - [CharBoundaryCategory.LineBreakCR]: 10, - [CharBoundaryCategory.LineBreakLF]: 10, -}; - -function getCategoryBoundaryScore(category: CharBoundaryCategory): number { - return score[category]; -} - -function getCategory(charCode: number): CharBoundaryCategory { - if (charCode === CharCode.LineFeed) { - return CharBoundaryCategory.LineBreakLF; - } else if (charCode === CharCode.CarriageReturn) { - return CharBoundaryCategory.LineBreakCR; - } else if (isSpace(charCode)) { - return CharBoundaryCategory.Space; - } else if (charCode >= CharCode.a && charCode <= CharCode.z) { - return CharBoundaryCategory.WordLower; - } else if (charCode >= CharCode.A && charCode <= CharCode.Z) { - return CharBoundaryCategory.WordUpper; - } else if (charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9) { - return CharBoundaryCategory.WordNumber; - } else if (charCode === -1) { - return CharBoundaryCategory.End; - } else { - return CharBoundaryCategory.Other; - } -} - -function isSpace(charCode: number): boolean { - return charCode === CharCode.Space || charCode === CharCode.Tab; -} - -const chrKeys = new Map(); -function getKey(chr: string): number { - let key = chrKeys.get(chr); - if (key === undefined) { - key = chrKeys.size; - chrKeys.set(chr, key); - } - return key; -} - -class LineRangeFragment { - private readonly totalCount: number; - private readonly histogram: number[] = []; - constructor( - public readonly range: LineRange, - public readonly lines: string[], - public readonly source: DetailedLineRangeMapping, - ) { - let counter = 0; - for (let i = range.startLineNumber - 1; i < range.endLineNumberExclusive - 1; i++) { - const line = lines[i]; - for (let j = 0; j < line.length; j++) { - counter++; - const chr = line[j]; - const key = getKey(chr); - this.histogram[key] = (this.histogram[key] || 0) + 1; - } - counter++; - const key = getKey('\n'); - this.histogram[key] = (this.histogram[key] || 0) + 1; - } - - this.totalCount = counter; - } - - public computeSimilarity(other: LineRangeFragment): number { - let sumDifferences = 0; - const maxLength = Math.max(this.histogram.length, other.histogram.length); - for (let i = 0; i < maxLength; i++) { - sumDifferences += Math.abs((this.histogram[i] ?? 0) - (other.histogram[i] ?? 0)); - } - return 1 - (sumDifferences / (this.totalCount + other.totalCount)); - } -} diff --git a/src/vs/editor/common/diff/algorithms/utils.ts b/src/vs/editor/common/diff/algorithms/utils.ts deleted file mode 100644 index e959afb86de..00000000000 --- a/src/vs/editor/common/diff/algorithms/utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export class Array2D { - private readonly array: T[] = []; - - constructor(public readonly width: number, public readonly height: number) { - this.array = new Array(width * height); - } - - get(x: number, y: number): T { - return this.array[x + y * this.width]; - } - - set(x: number, y: number, value: T): void { - this.array[x + y * this.width] = value; - } -} diff --git a/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts similarity index 99% rename from src/vs/editor/common/diff/algorithms/diffAlgorithm.ts rename to src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts index c38a84c2845..2d095c6209b 100644 --- a/src/vs/editor/common/diff/algorithms/diffAlgorithm.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts @@ -56,7 +56,7 @@ export class SequenceDiff { public readonly seq2Range: OffsetRange, ) { } - public reverse(): SequenceDiff { + public swap(): SequenceDiff { return new SequenceDiff(this.seq2Range, this.seq1Range); } diff --git a/src/vs/editor/common/diff/algorithms/dynamicProgrammingDiffing.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.ts similarity index 95% rename from src/vs/editor/common/diff/algorithms/dynamicProgrammingDiffing.ts rename to src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.ts index 2212ebef27e..f27644435b0 100644 --- a/src/vs/editor/common/diff/algorithms/dynamicProgrammingDiffing.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; -import { IDiffAlgorithm, SequenceDiff, ISequence, ITimeout, InfiniteTimeout, DiffAlgorithmResult } from 'vs/editor/common/diff/algorithms/diffAlgorithm'; -import { Array2D } from 'vs/editor/common/diff/algorithms/utils'; +import { IDiffAlgorithm, SequenceDiff, ISequence, ITimeout, InfiniteTimeout, DiffAlgorithmResult } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; +import { Array2D } from 'vs/editor/common/diff/defaultLinesDiffComputer/utils'; /** * A O(MN) diffing algorithm that supports a score function. diff --git a/src/vs/editor/common/diff/algorithms/myersDiffAlgorithm.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts similarity index 97% rename from src/vs/editor/common/diff/algorithms/myersDiffAlgorithm.ts rename to src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts index 049c5ff157c..0f3b64004cd 100644 --- a/src/vs/editor/common/diff/algorithms/myersDiffAlgorithm.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; -import { DiffAlgorithmResult, IDiffAlgorithm, ISequence, ITimeout, InfiniteTimeout, SequenceDiff } from 'vs/editor/common/diff/algorithms/diffAlgorithm'; +import { DiffAlgorithmResult, IDiffAlgorithm, ISequence, ITimeout, InfiniteTimeout, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; /** * An O(ND) diff algorithm that has a quadratic space worst-case complexity. diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMoves.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMoves.ts new file mode 100644 index 00000000000..a148f75de3e --- /dev/null +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMoves.ts @@ -0,0 +1,314 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITimeout, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; +import { DetailedLineRangeMapping, LineRangeMapping } from '../rangeMapping'; +import { pushMany, compareBy, numberComparator, reverseOrder } from 'vs/base/common/arrays'; +import { MonotonousArray, findLastMonotonous } from 'vs/base/common/arraysFind'; +import { SetMap } from 'vs/base/common/collections'; +import { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; +import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; +import { LineRangeFragment, isSpace } from 'vs/editor/common/diff/defaultLinesDiffComputer/utils'; +import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; + +export function computeMoves( + changes: DetailedLineRangeMapping[], + originalLines: string[], + modifiedLines: string[], + hashedOriginalLines: number[], + hashedModifiedLines: number[], + timeout: ITimeout +): LineRangeMapping[] { + let { moves, excludedChanges } = computeMovesFromSimpleDeletionsToSimpleInsertions(changes, originalLines, modifiedLines, timeout); + + if (!timeout.isValid()) { return []; } + + const filteredChanges = changes.filter(c => !excludedChanges.has(c)); + const unchangedMoves = computeUnchangedMoves(filteredChanges, hashedOriginalLines, hashedModifiedLines, originalLines, modifiedLines, timeout); + pushMany(moves, unchangedMoves); + + moves = joinCloseConsecutiveMoves(moves); + // Ignore too short moves + moves = moves.filter(current => { + const originalText = current.original.toOffsetRange().slice(originalLines).map(l => l.trim()).join('\n'); + return originalText.length >= 10; + }); + moves = removeMovesInSameDiff(changes, moves); + + return moves; +} + +function computeMovesFromSimpleDeletionsToSimpleInsertions( + changes: DetailedLineRangeMapping[], + originalLines: string[], + modifiedLines: string[], + timeout: ITimeout, +) { + const moves: LineRangeMapping[] = []; + + const deletions = changes + .filter(c => c.modified.isEmpty && c.original.length >= 3) + .map(d => new LineRangeFragment(d.original, originalLines, d)); + const insertions = new Set(changes + .filter(c => c.original.isEmpty && c.modified.length >= 3) + .map(d => new LineRangeFragment(d.modified, modifiedLines, d))); + + const excludedChanges = new Set(); + + for (const deletion of deletions) { + let highestSimilarity = -1; + let best: LineRangeFragment | undefined; + for (const insertion of insertions) { + const similarity = deletion.computeSimilarity(insertion); + if (similarity > highestSimilarity) { + highestSimilarity = similarity; + best = insertion; + } + } + + if (highestSimilarity > 0.90 && best) { + insertions.delete(best); + moves.push(new LineRangeMapping(deletion.range, best.range)); + excludedChanges.add(deletion.source); + excludedChanges.add(best.source); + } + + if (!timeout.isValid()) { + return { moves, excludedChanges }; + } + } + + return { moves, excludedChanges }; +} + +function computeUnchangedMoves( + changes: DetailedLineRangeMapping[], + hashedOriginalLines: number[], + hashedModifiedLines: number[], + originalLines: string[], + modifiedLines: string[], + timeout: ITimeout, +) { + const moves: LineRangeMapping[] = []; + + const original3LineHashes = new SetMap(); + + for (const change of changes) { + for (let i = change.original.startLineNumber; i < change.original.endLineNumberExclusive - 2; i++) { + const key = `${hashedOriginalLines[i - 1]}:${hashedOriginalLines[i + 1 - 1]}:${hashedOriginalLines[i + 2 - 1]}`; + original3LineHashes.add(key, { range: new LineRange(i, i + 3) }); + } + } + + interface PossibleMapping { + modifiedLineRange: LineRange; + originalLineRange: LineRange; + } + + const possibleMappings: PossibleMapping[] = []; + + changes.sort(compareBy(c => c.modified.startLineNumber, numberComparator)); + + for (const change of changes) { + let lastMappings: PossibleMapping[] = []; + for (let i = change.modified.startLineNumber; i < change.modified.endLineNumberExclusive - 2; i++) { + const key = `${hashedModifiedLines[i - 1]}:${hashedModifiedLines[i + 1 - 1]}:${hashedModifiedLines[i + 2 - 1]}`; + const currentModifiedRange = new LineRange(i, i + 3); + + const nextMappings: PossibleMapping[] = []; + original3LineHashes.forEach(key, ({ range }) => { + for (const lastMapping of lastMappings) { + // does this match extend some last match? + if (lastMapping.originalLineRange.endLineNumberExclusive + 1 === range.endLineNumberExclusive && + lastMapping.modifiedLineRange.endLineNumberExclusive + 1 === currentModifiedRange.endLineNumberExclusive) { + lastMapping.originalLineRange = new LineRange(lastMapping.originalLineRange.startLineNumber, range.endLineNumberExclusive); + lastMapping.modifiedLineRange = new LineRange(lastMapping.modifiedLineRange.startLineNumber, currentModifiedRange.endLineNumberExclusive); + nextMappings.push(lastMapping); + return; + } + } + + const mapping: PossibleMapping = { + modifiedLineRange: currentModifiedRange, + originalLineRange: range, + }; + possibleMappings.push(mapping); + nextMappings.push(mapping); + }); + lastMappings = nextMappings; + } + + if (!timeout.isValid()) { + return []; + } + } + + possibleMappings.sort(reverseOrder(compareBy(m => m.modifiedLineRange.length, numberComparator))); + + const modifiedSet = new LineRangeSet(); + const originalSet = new LineRangeSet(); + + for (const mapping of possibleMappings) { + + const diffOrigToMod = mapping.modifiedLineRange.startLineNumber - mapping.originalLineRange.startLineNumber; + const modifiedSections = modifiedSet.subtractFrom(mapping.modifiedLineRange); + const originalTranslatedSections = originalSet.subtractFrom(mapping.originalLineRange).getWithDelta(diffOrigToMod); + + const modifiedIntersectedSections = modifiedSections.getIntersection(originalTranslatedSections); + + for (const s of modifiedIntersectedSections.ranges) { + if (s.length < 3) { + continue; + } + const modifiedLineRange = s; + const originalLineRange = s.delta(-diffOrigToMod); + + moves.push(new LineRangeMapping(originalLineRange, modifiedLineRange)); + + modifiedSet.addRange(modifiedLineRange); + originalSet.addRange(originalLineRange); + } + } + + moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); + + const monotonousChanges = new MonotonousArray(changes); + for (let i = 0; i < moves.length; i++) { + const move = moves[i]; + const firstTouchingChangeOrig = monotonousChanges.findLastMonotonous(c => c.original.startLineNumber <= move.original.startLineNumber)!; + const firstTouchingChangeMod = findLastMonotonous(changes, c => c.modified.startLineNumber <= move.modified.startLineNumber)!; + const linesAbove = Math.max( + move.original.startLineNumber - firstTouchingChangeOrig.original.startLineNumber, + move.modified.startLineNumber - firstTouchingChangeMod.modified.startLineNumber + ); + + const lastTouchingChangeOrig = monotonousChanges.findLastMonotonous(c => c.original.startLineNumber < move.original.endLineNumberExclusive)!; + const lastTouchingChangeMod = findLastMonotonous(changes, c => c.modified.startLineNumber < move.modified.endLineNumberExclusive)!; + const linesBelow = Math.max( + lastTouchingChangeOrig.original.endLineNumberExclusive - move.original.endLineNumberExclusive, + lastTouchingChangeMod.modified.endLineNumberExclusive - move.modified.endLineNumberExclusive + ); + + let extendToTop: number; + for (extendToTop = 0; extendToTop < linesAbove; extendToTop++) { + const origLine = move.original.startLineNumber - extendToTop - 1; + const modLine = move.modified.startLineNumber - extendToTop - 1; + if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) { + break; + } + if (!areLinesSimilar(originalLines[origLine - 1], modifiedLines[modLine - 1], timeout)) { + break; + } + } + + if (extendToTop > 0) { + originalSet.addRange(new LineRange(move.original.startLineNumber - extendToTop, move.original.startLineNumber)); + modifiedSet.addRange(new LineRange(move.modified.startLineNumber - extendToTop, move.modified.startLineNumber)); + } + + let extendToBottom: number; + for (extendToBottom = 0; extendToBottom < linesBelow; extendToBottom++) { + const origLine = move.original.endLineNumberExclusive + extendToBottom; + const modLine = move.modified.endLineNumberExclusive + extendToBottom; + if (modifiedSet.contains(modLine) || originalSet.contains(origLine)) { + break; + } + if (!areLinesSimilar(originalLines[origLine - 1], modifiedLines[modLine - 1], timeout)) { + break; + } + } + + if (extendToBottom > 0) { + originalSet.addRange(new LineRange(move.original.endLineNumberExclusive, move.original.endLineNumberExclusive + extendToBottom)); + modifiedSet.addRange(new LineRange(move.modified.endLineNumberExclusive, move.modified.endLineNumberExclusive + extendToBottom)); + } + + if (extendToTop > 0 || extendToBottom > 0) { + moves[i] = new LineRangeMapping( + new LineRange(move.original.startLineNumber - extendToTop, move.original.endLineNumberExclusive + extendToBottom), + new LineRange(move.modified.startLineNumber - extendToTop, move.modified.endLineNumberExclusive + extendToBottom), + ); + } + } + + return moves; +} + +function areLinesSimilar(line1: string, line2: string, timeout: ITimeout): boolean { + if (line1.trim() === line2.trim()) { return true; } + if (line1.length > 300 && line2.length > 300) { return false; } + + const myersDiffingAlgorithm = new MyersDiffAlgorithm(); + const result = myersDiffingAlgorithm.compute( + new LinesSliceCharSequence([line1], new OffsetRange(0, 1), false), + new LinesSliceCharSequence([line2], new OffsetRange(0, 1), false), + timeout + ); + let commonNonSpaceCharCount = 0; + const inverted = SequenceDiff.invert(result.diffs, line1.length); + for (const seq of inverted) { + seq.seq1Range.forEach(idx => { + if (!isSpace(line1.charCodeAt(idx))) { + commonNonSpaceCharCount++; + } + }); + } + + function countNonWsChars(str: string): number { + let count = 0; + for (let i = 0; i < line1.length; i++) { + if (!isSpace(str.charCodeAt(i))) { + count++; + } + } + return count; + } + + const longerLineLength = countNonWsChars(line1.length > line2.length ? line1 : line2); + const r = commonNonSpaceCharCount / longerLineLength > 0.6 && longerLineLength > 10; + return r; +} + +function joinCloseConsecutiveMoves(moves: LineRangeMapping[]): LineRangeMapping[] { + if (moves.length === 0) { + return moves; + } + + moves.sort(compareBy(m => m.original.startLineNumber, numberComparator)); + + const result = [moves[0]]; + for (let i = 1; i < moves.length; i++) { + const last = result[result.length - 1]; + const current = moves[i]; + + const originalDist = current.original.startLineNumber - last.original.endLineNumberExclusive; + const modifiedDist = current.modified.startLineNumber - last.modified.endLineNumberExclusive; + const currentMoveAfterLast = originalDist >= 0 && modifiedDist >= 0; + + if (currentMoveAfterLast && originalDist + modifiedDist <= 2) { + result[result.length - 1] = last.join(current); + continue; + } + + result.push(current); + } + return result; +} + +function removeMovesInSameDiff(changes: DetailedLineRangeMapping[], moves: LineRangeMapping[]) { + const changesMonotonous = new MonotonousArray(changes); + moves = moves.filter(m => { + const diffBeforeOriginalMove = changesMonotonous.findLastMonotonous(c => c.original.endLineNumberExclusive <= m.original.startLineNumber) + || new LineRangeMapping(new LineRange(1, 1), new LineRange(1, 1)); + + const modifiedDistToPrevDiff = m.modified.startLineNumber - diffBeforeOriginalMove.modified.endLineNumberExclusive; + const originalDistToPrevDiff = m.original.startLineNumber - diffBeforeOriginalMove.original.endLineNumberExclusive; + + const differentDistances = modifiedDistToPrevDiff !== originalDistToPrevDiff; + return differentDistances; + }); + return moves; +} diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts new file mode 100644 index 00000000000..c94f8cea008 --- /dev/null +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts @@ -0,0 +1,310 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { equals, groupAdjacentBy } from 'vs/base/common/arrays'; +import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { DateTimeout, ITimeout, InfiniteTimeout, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; +import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing'; +import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; +import { computeMoves } from 'vs/editor/common/diff/defaultLinesDiffComputer/computeMoves'; +import { extendDiffsToEntireWordIfAppropriate, optimizeSequenceDiffs, removeRandomLineMatches, removeRandomMatches, smoothenSequenceDiffs } from 'vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations'; +import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; +import { DetailedLineRangeMapping, RangeMapping } from '../rangeMapping'; +import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; +import { LineSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/lineSequence'; + +export class DefaultLinesDiffComputer implements ILinesDiffComputer { + private readonly dynamicProgrammingDiffing = new DynamicProgrammingDiffing(); + private readonly myersDiffingAlgorithm = new MyersDiffAlgorithm(); + + computeDiff(originalLines: string[], modifiedLines: string[], options: ILinesDiffComputerOptions): LinesDiff { + if (originalLines.length <= 1 && equals(originalLines, modifiedLines, (a, b) => a === b)) { + return new LinesDiff([], [], false); + } + + if (originalLines.length === 1 && originalLines[0].length === 0 || modifiedLines.length === 1 && modifiedLines[0].length === 0) { + return new LinesDiff([ + new DetailedLineRangeMapping( + new LineRange(1, originalLines.length + 1), + new LineRange(1, modifiedLines.length + 1), + [ + new RangeMapping( + new Range(1, 1, originalLines.length, originalLines[0].length + 1), + new Range(1, 1, modifiedLines.length, modifiedLines[0].length + 1) + ) + ] + ) + ], [], false); + } + + const timeout = options.maxComputationTimeMs === 0 ? InfiniteTimeout.instance : new DateTimeout(options.maxComputationTimeMs); + const considerWhitespaceChanges = !options.ignoreTrimWhitespace; + + const perfectHashes = new Map(); + function getOrCreateHash(text: string): number { + let hash = perfectHashes.get(text); + if (hash === undefined) { + hash = perfectHashes.size; + perfectHashes.set(text, hash); + } + return hash; + } + + const originalLinesHashes = originalLines.map((l) => getOrCreateHash(l.trim())); + const modifiedLinesHashes = modifiedLines.map((l) => getOrCreateHash(l.trim())); + + const sequence1 = new LineSequence(originalLinesHashes, originalLines); + const sequence2 = new LineSequence(modifiedLinesHashes, modifiedLines); + + const lineAlignmentResult = (() => { + if (sequence1.length + sequence2.length < 1700) { + // Use the improved algorithm for small files + return this.dynamicProgrammingDiffing.compute( + sequence1, + sequence2, + timeout, + (offset1, offset2) => + originalLines[offset1] === modifiedLines[offset2] + ? modifiedLines[offset2].length === 0 + ? 0.1 + : 1 + Math.log(1 + modifiedLines[offset2].length) + : 0.99 + ); + } + + return this.myersDiffingAlgorithm.compute( + sequence1, + sequence2 + ); + })(); + + let lineAlignments = lineAlignmentResult.diffs; + let hitTimeout = lineAlignmentResult.hitTimeout; + lineAlignments = optimizeSequenceDiffs(sequence1, sequence2, lineAlignments); + lineAlignments = removeRandomLineMatches(sequence1, sequence2, lineAlignments); + + const alignments: RangeMapping[] = []; + + const scanForWhitespaceChanges = (equalLinesCount: number) => { + if (!considerWhitespaceChanges) { + return; + } + + for (let i = 0; i < equalLinesCount; i++) { + const seq1Offset = seq1LastStart + i; + const seq2Offset = seq2LastStart + i; + if (originalLines[seq1Offset] !== modifiedLines[seq2Offset]) { + // This is because of whitespace changes, diff these lines + const characterDiffs = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( + new OffsetRange(seq1Offset, seq1Offset + 1), + new OffsetRange(seq2Offset, seq2Offset + 1), + ), timeout, considerWhitespaceChanges); + for (const a of characterDiffs.mappings) { + alignments.push(a); + } + if (characterDiffs.hitTimeout) { + hitTimeout = true; + } + } + } + }; + + let seq1LastStart = 0; + let seq2LastStart = 0; + + for (const diff of lineAlignments) { + assertFn(() => diff.seq1Range.start - seq1LastStart === diff.seq2Range.start - seq2LastStart); + + const equalLinesCount = diff.seq1Range.start - seq1LastStart; + + scanForWhitespaceChanges(equalLinesCount); + + seq1LastStart = diff.seq1Range.endExclusive; + seq2LastStart = diff.seq2Range.endExclusive; + + const characterDiffs = this.refineDiff(originalLines, modifiedLines, diff, timeout, considerWhitespaceChanges); + if (characterDiffs.hitTimeout) { + hitTimeout = true; + } + for (const a of characterDiffs.mappings) { + alignments.push(a); + } + } + + scanForWhitespaceChanges(originalLines.length - seq1LastStart); + + const changes = lineRangeMappingFromRangeMappings(alignments, originalLines, modifiedLines); + + let moves: MovedText[] = []; + if (options.computeMoves) { + moves = this.computeMoves(changes, originalLines, modifiedLines, originalLinesHashes, modifiedLinesHashes, timeout, considerWhitespaceChanges); + } + + // Make sure all ranges are valid + assertFn(() => { + function validatePosition(pos: Position, lines: string[]): boolean { + if (pos.lineNumber < 1 || pos.lineNumber > lines.length) { return false; } + const line = lines[pos.lineNumber - 1]; + if (pos.column < 1 || pos.column > line.length + 1) { return false; } + return true; + } + + function validateRange(range: LineRange, lines: string[]): boolean { + if (range.startLineNumber < 1 || range.startLineNumber > lines.length + 1) { return false; } + if (range.endLineNumberExclusive < 1 || range.endLineNumberExclusive > lines.length + 1) { return false; } + return true; + } + + for (const c of changes) { + if (!c.innerChanges) { return false; } + for (const ic of c.innerChanges) { + const valid = validatePosition(ic.modifiedRange.getStartPosition(), modifiedLines) && validatePosition(ic.modifiedRange.getEndPosition(), modifiedLines) && + validatePosition(ic.originalRange.getStartPosition(), originalLines) && validatePosition(ic.originalRange.getEndPosition(), originalLines); + if (!valid) { return false; } + } + if (!validateRange(c.modified, modifiedLines) || !validateRange(c.original, originalLines)) { + return false; + } + } + return true; + }); + + return new LinesDiff(changes, moves, hitTimeout); + } + + private computeMoves( + changes: DetailedLineRangeMapping[], + originalLines: string[], + modifiedLines: string[], + hashedOriginalLines: number[], + hashedModifiedLines: number[], + timeout: ITimeout, + considerWhitespaceChanges: boolean, + ): MovedText[] { + const moves = computeMoves( + changes, + originalLines, + modifiedLines, + hashedOriginalLines, + hashedModifiedLines, + timeout, + ); + const movesWithDiffs = moves.map(m => { + const moveChanges = this.refineDiff(originalLines, modifiedLines, new SequenceDiff( + m.original.toOffsetRange(), + m.modified.toOffsetRange(), + ), timeout, considerWhitespaceChanges); + const mappings = lineRangeMappingFromRangeMappings(moveChanges.mappings, originalLines, modifiedLines, true); + return new MovedText(m, mappings); + }); + return movesWithDiffs; + } + + private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } { + const slice1 = new LinesSliceCharSequence(originalLines, diff.seq1Range, considerWhitespaceChanges); + const slice2 = new LinesSliceCharSequence(modifiedLines, diff.seq2Range, considerWhitespaceChanges); + + const diffResult = slice1.length + slice2.length < 500 + ? this.dynamicProgrammingDiffing.compute(slice1, slice2, timeout) + : this.myersDiffingAlgorithm.compute(slice1, slice2, timeout); + + let diffs = diffResult.diffs; + diffs = optimizeSequenceDiffs(slice1, slice2, diffs); + diffs = extendDiffsToEntireWordIfAppropriate(slice1, slice2, diffs); + diffs = smoothenSequenceDiffs(slice1, slice2, diffs); + diffs = removeRandomMatches(slice1, slice2, diffs); + + const result = diffs.map( + (d) => + new RangeMapping( + slice1.translateRange(d.seq1Range), + slice2.translateRange(d.seq2Range) + ) + ); + + // Assert: result applied on original should be the same as diff applied to original + + return { + mappings: result, + hitTimeout: diffResult.hitTimeout, + }; + } +} + +export function lineRangeMappingFromRangeMappings(alignments: RangeMapping[], originalLines: string[], modifiedLines: string[], dontAssertStartLine: boolean = false): DetailedLineRangeMapping[] { + const changes: DetailedLineRangeMapping[] = []; + for (const g of groupAdjacentBy( + alignments.map(a => getLineRangeMapping(a, originalLines, modifiedLines)), + (a1, a2) => + a1.original.overlapOrTouch(a2.original) + || a1.modified.overlapOrTouch(a2.modified) + )) { + const first = g[0]; + const last = g[g.length - 1]; + + changes.push(new DetailedLineRangeMapping( + first.original.join(last.original), + first.modified.join(last.modified), + g.map(a => a.innerChanges![0]), + )); + } + + assertFn(() => { + if (!dontAssertStartLine) { + if (changes.length > 0 && changes[0].original.startLineNumber !== changes[0].modified.startLineNumber) { + return false; + } + } + return checkAdjacentItems(changes, + (m1, m2) => m2.original.startLineNumber - m1.original.endLineNumberExclusive === m2.modified.startLineNumber - m1.modified.endLineNumberExclusive && + // There has to be an unchanged line in between (otherwise both diffs should have been joined) + m1.original.endLineNumberExclusive < m2.original.startLineNumber && + m1.modified.endLineNumberExclusive < m2.modified.startLineNumber, + ); + }); + + return changes; +} + +export function getLineRangeMapping(rangeMapping: RangeMapping, originalLines: string[], modifiedLines: string[]): DetailedLineRangeMapping { + let lineStartDelta = 0; + let lineEndDelta = 0; + + // rangeMapping describes the edit that replaces `rangeMapping.originalRange` with `newText := getText(modifiedLines, rangeMapping.modifiedRange)`. + + // original: ]xxx \n <- this line is not modified + // modified: ]xx \n + if (rangeMapping.modifiedRange.endColumn === 1 && rangeMapping.originalRange.endColumn === 1 + && rangeMapping.originalRange.startLineNumber + lineStartDelta <= rangeMapping.originalRange.endLineNumber + && rangeMapping.modifiedRange.startLineNumber + lineStartDelta <= rangeMapping.modifiedRange.endLineNumber) { + // We can only do this if the range is not empty yet + lineEndDelta = -1; + } + + // original: xxx[ \n <- this line is not modified + // modified: xxx[ \n + if (rangeMapping.modifiedRange.startColumn - 1 >= modifiedLines[rangeMapping.modifiedRange.startLineNumber - 1].length + && rangeMapping.originalRange.startColumn - 1 >= originalLines[rangeMapping.originalRange.startLineNumber - 1].length + && rangeMapping.originalRange.startLineNumber <= rangeMapping.originalRange.endLineNumber + lineEndDelta + && rangeMapping.modifiedRange.startLineNumber <= rangeMapping.modifiedRange.endLineNumber + lineEndDelta) { + // We can only do this if the range is not empty yet + lineStartDelta = 1; + } + + const originalLineRange = new LineRange( + rangeMapping.originalRange.startLineNumber + lineStartDelta, + rangeMapping.originalRange.endLineNumber + 1 + lineEndDelta + ); + const modifiedLineRange = new LineRange( + rangeMapping.modifiedRange.startLineNumber + lineStartDelta, + rangeMapping.modifiedRange.endLineNumber + 1 + lineEndDelta + ); + + return new DetailedLineRangeMapping(originalLineRange, modifiedLineRange, [rangeMapping]); +} diff --git a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts similarity index 72% rename from src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts rename to src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts index cef14d466c8..d39fc3c93e7 100644 --- a/src/vs/editor/common/diff/algorithms/joinSequenceDiffs.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts @@ -4,177 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { OffsetRange } from 'vs/editor/common/core/offsetRange'; -import { ISequence, SequenceDiff } from 'vs/editor/common/diff/algorithms/diffAlgorithm'; -import { LineSequence, LinesSliceCharSequence } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { ISequence, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; +import { LineSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/lineSequence'; +import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; export function optimizeSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { let result = sequenceDiffs; - result = joinSequenceDiffs(sequence1, sequence2, result); + result = joinSequenceDiffsByShifting(sequence1, sequence2, result); result = shiftSequenceDiffs(sequence1, sequence2, result); return result; } -export function smoothenSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { - const result: SequenceDiff[] = []; - for (const s of sequenceDiffs) { - const last = result[result.length - 1]; - if (!last) { - result.push(s); - continue; - } - - if (s.seq1Range.start - last.seq1Range.endExclusive <= 2 || s.seq2Range.start - last.seq2Range.endExclusive <= 2) { - result[result.length - 1] = new SequenceDiff(last.seq1Range.join(s.seq1Range), last.seq2Range.join(s.seq2Range)); - } else { - result.push(s); - } - } - - return result; -} - -export function removeRandomLineMatches(sequence1: LineSequence, _sequence2: LineSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { - let diffs = sequenceDiffs; - if (diffs.length === 0) { - return diffs; - } - - let counter = 0; - let shouldRepeat: boolean; - do { - shouldRepeat = false; - - const result: SequenceDiff[] = [ - diffs[0] - ]; - - for (let i = 1; i < diffs.length; i++) { - const cur = diffs[i]; - const lastResult = result[result.length - 1]; - - function shouldJoinDiffs(before: SequenceDiff, after: SequenceDiff): boolean { - const unchangedRange = new OffsetRange(lastResult.seq1Range.endExclusive, cur.seq1Range.start); - - const unchangedText = sequence1.getText(unchangedRange); - const unchangedTextWithoutWs = unchangedText.replace(/\s/g, ''); - if (unchangedTextWithoutWs.length <= 4 - && (before.seq1Range.length + before.seq2Range.length > 5 || after.seq1Range.length + after.seq2Range.length > 5)) { - return true; - } - - return false; - } - - const shouldJoin = shouldJoinDiffs(lastResult, cur); - if (shouldJoin) { - shouldRepeat = true; - result[result.length - 1] = result[result.length - 1].join(cur); - } else { - result.push(cur); - } - } - - diffs = result; - } while (counter++ < 10 && shouldRepeat); - - return diffs; -} - - -export function removeRandomMatches(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { - let diffs = sequenceDiffs; - if (diffs.length === 0) { - return diffs; - } - - let counter = 0; - let shouldRepeat: boolean; - do { - shouldRepeat = false; - - const result: SequenceDiff[] = [ - diffs[0] - ]; - - for (let i = 1; i < diffs.length; i++) { - const cur = diffs[i]; - const lastResult = result[result.length - 1]; - - function shouldJoinDiffs(before: SequenceDiff, after: SequenceDiff): boolean { - const unchangedRange = new OffsetRange(lastResult.seq1Range.endExclusive, cur.seq1Range.start); - - const unchangedLineCount = sequence1.countLinesIn(unchangedRange); - if (unchangedLineCount > 5 || unchangedRange.length > 500) { - return false; - } - - const unchangedText = sequence1.getText(unchangedRange).trim(); - if (unchangedText.length > 20 || unchangedText.split(/\r\n|\r|\n/).length > 1) { - return false; - } - - const beforeLineCount1 = sequence1.countLinesIn(before.seq1Range); - const beforeSeq1Length = before.seq1Range.length; - const beforeLineCount2 = sequence2.countLinesIn(before.seq2Range); - const beforeSeq2Length = before.seq2Range.length; - - const afterLineCount1 = sequence1.countLinesIn(after.seq1Range); - const afterSeq1Length = after.seq1Range.length; - const afterLineCount2 = sequence2.countLinesIn(after.seq2Range); - const afterSeq2Length = after.seq2Range.length; - - // TODO: Maybe a neural net can be used to derive the result from these numbers - - const max = 2 * 40 + 50; - function cap(v: number): number { - return Math.min(v, max); - } - - if (Math.pow(Math.pow(cap(beforeLineCount1 * 40 + beforeSeq1Length), 1.5) + Math.pow(cap(beforeLineCount2 * 40 + beforeSeq2Length), 1.5), 1.5) - + Math.pow(Math.pow(cap(afterLineCount1 * 40 + afterSeq1Length), 1.5) + Math.pow(cap(afterLineCount2 * 40 + afterSeq2Length), 1.5), 1.5) > ((max ** 1.5) ** 1.5) * 1.3) { - return true; - } - return false; - } - - const shouldJoin = shouldJoinDiffs(lastResult, cur); - if (shouldJoin) { - shouldRepeat = true; - result[result.length - 1] = result[result.length - 1].join(cur); - } else { - result.push(cur); - } - } - - diffs = result; - } while (counter++ < 10 && shouldRepeat); - - // Remove short suffixes/prefixes - for (let i = 0; i < diffs.length; i++) { - const cur = diffs[i]; - - let range1 = cur.seq1Range; - let range2 = cur.seq2Range; - - const fullRange1 = sequence1.extendToFullLines(cur.seq1Range); - const prefix = sequence1.getText(new OffsetRange(fullRange1.start, cur.seq1Range.start)); - if (prefix.length > 0 && prefix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100) { - range1 = cur.seq1Range.deltaStart(-prefix.length); - range2 = cur.seq2Range.deltaStart(-prefix.length); - } - - const suffix = sequence1.getText(new OffsetRange(cur.seq1Range.endExclusive, fullRange1.endExclusive)); - if (suffix.length > 0 && (suffix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 150)) { - range1 = range1.deltaEnd(suffix.length); - range2 = range2.deltaEnd(suffix.length); - } - - diffs[i] = new SequenceDiff(range1, range2); - } - - return diffs; -} - /** * This function fixes issues like this: * ``` @@ -187,7 +27,7 @@ export function removeRandomMatches(sequence1: LinesSliceCharSequence, sequence2 * Computed diff: [ {Add "," after Bar}, {Add "Foo " after space} } * Improved diff: [{Add ", Foo" after Bar}] */ -export function joinSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +function joinSequenceDiffsByShifting(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { if (sequenceDiffs.length === 0) { return sequenceDiffs; } @@ -285,7 +125,7 @@ export function joinSequenceDiffs(sequence1: ISequence, sequence2: ISequence, se // -> // collectBrackets(level + 1, [levelPerBracket + 1, ]levelPerBracketType); -export function shiftSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +function shiftSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { if (!sequence1.getBoundaryScore || !sequence2.getBoundaryScore) { return sequenceDiffs; } @@ -301,7 +141,7 @@ export function shiftSequenceDiffs(sequence1: ISequence, sequence2: ISequence, s if (diff.seq1Range.isEmpty) { sequenceDiffs[i] = shiftDiffToBetterPosition(diff, sequence1, sequence2, seq1ValidRange, seq2ValidRange); } else if (diff.seq2Range.isEmpty) { - sequenceDiffs[i] = shiftDiffToBetterPosition(diff.reverse(), sequence2, sequence1, seq2ValidRange, seq1ValidRange).reverse(); + sequenceDiffs[i] = shiftDiffToBetterPosition(diff.swap(), sequence2, sequence1, seq2ValidRange, seq1ValidRange).swap(); } } @@ -355,3 +195,258 @@ function shiftDiffToBetterPosition(diff: SequenceDiff, sequence1: ISequence, seq return diff.delta(bestDelta); } + +export function smoothenSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { + const result: SequenceDiff[] = []; + for (const s of sequenceDiffs) { + const last = result[result.length - 1]; + if (!last) { + result.push(s); + continue; + } + + if (s.seq1Range.start - last.seq1Range.endExclusive <= 2 || s.seq2Range.start - last.seq2Range.endExclusive <= 2) { + result[result.length - 1] = new SequenceDiff(last.seq1Range.join(s.seq1Range), last.seq2Range.join(s.seq2Range)); + } else { + result.push(s); + } + } + + return result; +} + +export function extendDiffsToEntireWordIfAppropriate(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { + const additional: SequenceDiff[] = []; + + let lastModifiedWord: { added: number; deleted: number; count: number; s1Range: OffsetRange; s2Range: OffsetRange } | undefined = undefined; + + function maybePushWordToAdditional() { + if (!lastModifiedWord) { + return; + } + + const originalLength1 = lastModifiedWord.s1Range.length - lastModifiedWord.deleted; + const originalLength2 = lastModifiedWord.s2Range.length - lastModifiedWord.added; + if (originalLength1 !== originalLength2) { + // TODO figure out why this happens + } + + if (Math.max(lastModifiedWord.deleted, lastModifiedWord.added) + (lastModifiedWord.count - 1) > originalLength1) { + additional.push(new SequenceDiff(lastModifiedWord.s1Range, lastModifiedWord.s2Range)); + } + + lastModifiedWord = undefined; + } + + for (const s of sequenceDiffs) { + function processWord(s1Range: OffsetRange, s2Range: OffsetRange) { + if (!lastModifiedWord || !lastModifiedWord.s1Range.containsRange(s1Range) || !lastModifiedWord.s2Range.containsRange(s2Range)) { + if (lastModifiedWord && !(lastModifiedWord.s1Range.endExclusive < s1Range.start && lastModifiedWord.s2Range.endExclusive < s2Range.start)) { + const s1Added = OffsetRange.tryCreate(lastModifiedWord.s1Range.endExclusive, s1Range.start); + const s2Added = OffsetRange.tryCreate(lastModifiedWord.s2Range.endExclusive, s2Range.start); + lastModifiedWord.deleted += s1Added?.length ?? 0; + lastModifiedWord.added += s2Added?.length ?? 0; + + lastModifiedWord.s1Range = lastModifiedWord.s1Range.join(s1Range); + lastModifiedWord.s2Range = lastModifiedWord.s2Range.join(s2Range); + } else { + maybePushWordToAdditional(); + lastModifiedWord = { added: 0, deleted: 0, count: 0, s1Range: s1Range, s2Range: s2Range }; + } + } + + const changedS1 = s1Range.intersect(s.seq1Range); + const changedS2 = s2Range.intersect(s.seq2Range); + lastModifiedWord.count++; + lastModifiedWord.deleted += changedS1?.length ?? 0; + lastModifiedWord.added += changedS2?.length ?? 0; + } + + const w1Before = sequence1.findWordContaining(s.seq1Range.start - 1); + const w2Before = sequence2.findWordContaining(s.seq2Range.start - 1); + + const w1After = sequence1.findWordContaining(s.seq1Range.endExclusive); + const w2After = sequence2.findWordContaining(s.seq2Range.endExclusive); + + if (w1Before && w1After && w2Before && w2After && w1Before.equals(w1After) && w2Before.equals(w2After)) { + processWord(w1Before, w2Before); + } else { + if (w1Before && w2Before) { + processWord(w1Before, w2Before); + } + if (w1After && w2After) { + processWord(w1After, w2After); + } + } + } + + maybePushWordToAdditional(); + + const merged = mergeSequenceDiffs(sequenceDiffs, additional); + return merged; +} + +function mergeSequenceDiffs(sequenceDiffs1: SequenceDiff[], sequenceDiffs2: SequenceDiff[]): SequenceDiff[] { + const result: SequenceDiff[] = []; + + while (sequenceDiffs1.length > 0 || sequenceDiffs2.length > 0) { + const sd1 = sequenceDiffs1[0]; + const sd2 = sequenceDiffs2[0]; + + let next: SequenceDiff; + if (sd1 && (!sd2 || sd1.seq1Range.start < sd2.seq1Range.start)) { + next = sequenceDiffs1.shift()!; + } else { + next = sequenceDiffs2.shift()!; + } + + if (result.length > 0 && result[result.length - 1].seq1Range.endExclusive >= next.seq1Range.start) { + result[result.length - 1] = result[result.length - 1].join(next); + } else { + result.push(next); + } + } + + return result; +} + +export function removeRandomLineMatches(sequence1: LineSequence, _sequence2: LineSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { + let diffs = sequenceDiffs; + if (diffs.length === 0) { + return diffs; + } + + let counter = 0; + let shouldRepeat: boolean; + do { + shouldRepeat = false; + + const result: SequenceDiff[] = [ + diffs[0] + ]; + + for (let i = 1; i < diffs.length; i++) { + const cur = diffs[i]; + const lastResult = result[result.length - 1]; + + function shouldJoinDiffs(before: SequenceDiff, after: SequenceDiff): boolean { + const unchangedRange = new OffsetRange(lastResult.seq1Range.endExclusive, cur.seq1Range.start); + + const unchangedText = sequence1.getText(unchangedRange); + const unchangedTextWithoutWs = unchangedText.replace(/\s/g, ''); + if (unchangedTextWithoutWs.length <= 4 + && (before.seq1Range.length + before.seq2Range.length > 5 || after.seq1Range.length + after.seq2Range.length > 5)) { + return true; + } + + return false; + } + + const shouldJoin = shouldJoinDiffs(lastResult, cur); + if (shouldJoin) { + shouldRepeat = true; + result[result.length - 1] = result[result.length - 1].join(cur); + } else { + result.push(cur); + } + } + + diffs = result; + } while (counter++ < 10 && shouldRepeat); + + return diffs; +} + +export function removeRandomMatches(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { + let diffs = sequenceDiffs; + if (diffs.length === 0) { + return diffs; + } + + let counter = 0; + let shouldRepeat: boolean; + do { + shouldRepeat = false; + + const result: SequenceDiff[] = [ + diffs[0] + ]; + + for (let i = 1; i < diffs.length; i++) { + const cur = diffs[i]; + const lastResult = result[result.length - 1]; + + function shouldJoinDiffs(before: SequenceDiff, after: SequenceDiff): boolean { + const unchangedRange = new OffsetRange(lastResult.seq1Range.endExclusive, cur.seq1Range.start); + + const unchangedLineCount = sequence1.countLinesIn(unchangedRange); + if (unchangedLineCount > 5 || unchangedRange.length > 500) { + return false; + } + + const unchangedText = sequence1.getText(unchangedRange).trim(); + if (unchangedText.length > 20 || unchangedText.split(/\r\n|\r|\n/).length > 1) { + return false; + } + + const beforeLineCount1 = sequence1.countLinesIn(before.seq1Range); + const beforeSeq1Length = before.seq1Range.length; + const beforeLineCount2 = sequence2.countLinesIn(before.seq2Range); + const beforeSeq2Length = before.seq2Range.length; + + const afterLineCount1 = sequence1.countLinesIn(after.seq1Range); + const afterSeq1Length = after.seq1Range.length; + const afterLineCount2 = sequence2.countLinesIn(after.seq2Range); + const afterSeq2Length = after.seq2Range.length; + + // TODO: Maybe a neural net can be used to derive the result from these numbers + + const max = 2 * 40 + 50; + function cap(v: number): number { + return Math.min(v, max); + } + + if (Math.pow(Math.pow(cap(beforeLineCount1 * 40 + beforeSeq1Length), 1.5) + Math.pow(cap(beforeLineCount2 * 40 + beforeSeq2Length), 1.5), 1.5) + + Math.pow(Math.pow(cap(afterLineCount1 * 40 + afterSeq1Length), 1.5) + Math.pow(cap(afterLineCount2 * 40 + afterSeq2Length), 1.5), 1.5) > ((max ** 1.5) ** 1.5) * 1.3) { + return true; + } + return false; + } + + const shouldJoin = shouldJoinDiffs(lastResult, cur); + if (shouldJoin) { + shouldRepeat = true; + result[result.length - 1] = result[result.length - 1].join(cur); + } else { + result.push(cur); + } + } + + diffs = result; + } while (counter++ < 10 && shouldRepeat); + + // Remove short suffixes/prefixes + for (let i = 0; i < diffs.length; i++) { + const cur = diffs[i]; + + let range1 = cur.seq1Range; + let range2 = cur.seq2Range; + + const fullRange1 = sequence1.extendToFullLines(cur.seq1Range); + const prefix = sequence1.getText(new OffsetRange(fullRange1.start, cur.seq1Range.start)); + if (prefix.length > 0 && prefix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 100) { + range1 = cur.seq1Range.deltaStart(-prefix.length); + range2 = cur.seq2Range.deltaStart(-prefix.length); + } + + const suffix = sequence1.getText(new OffsetRange(cur.seq1Range.endExclusive, fullRange1.endExclusive)); + if (suffix.length > 0 && (suffix.trim().length <= 3 && cur.seq1Range.length + cur.seq2Range.length > 150)) { + range1 = range1.deltaEnd(suffix.length); + range2 = range2.deltaEnd(suffix.length); + } + + diffs[i] = new SequenceDiff(range1, range2); + } + + return diffs; +} diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts new file mode 100644 index 00000000000..fd48f598de0 --- /dev/null +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from 'vs/base/common/charCode'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; +import { ISequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; + +export class LineSequence implements ISequence { + constructor( + private readonly trimmedHash: number[], + private readonly lines: string[] + ) { } + + getElement(offset: number): number { + return this.trimmedHash[offset]; + } + + get length(): number { + return this.trimmedHash.length; + } + + getBoundaryScore(length: number): number { + const indentationBefore = length === 0 ? 0 : getIndentation(this.lines[length - 1]); + const indentationAfter = length === this.lines.length ? 0 : getIndentation(this.lines[length]); + return 1000 - (indentationBefore + indentationAfter); + } + + getText(range: OffsetRange): string { + return this.lines.slice(range.start, range.endExclusive).join('\n'); + } + + isStronglyEqual(offset1: number, offset2: number): boolean { + return this.lines[offset1] === this.lines[offset2]; + } +} + +function getIndentation(str: string): number { + let i = 0; + while (i < str.length && (str.charCodeAt(i) === CharCode.Space || str.charCodeAt(i) === CharCode.Tab)) { + i++; + } + return i; +} diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts new file mode 100644 index 00000000000..ca515f2cbbe --- /dev/null +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts @@ -0,0 +1,217 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { findLastIdxMonotonous, findLastMonotonous, findFirstMonotonous } from 'vs/base/common/arraysFind'; +import { CharCode } from 'vs/base/common/charCode'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { ISequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; +import { isSpace } from 'vs/editor/common/diff/defaultLinesDiffComputer/utils'; + +export class LinesSliceCharSequence implements ISequence { + private readonly elements: number[] = []; + private readonly firstCharOffsetByLine: number[] = []; + public readonly lineRange: OffsetRange; + // To account for trimming + private readonly additionalOffsetByLine: number[] = []; + + constructor(public readonly lines: string[], lineRange: OffsetRange, public readonly considerWhitespaceChanges: boolean) { + // This slice has to have lineRange.length many \n! (otherwise diffing against an empty slice will be problematic) + // (Unless it covers the entire document, in that case the other slice also has to cover the entire document ands it's okay) + + // If the slice covers the end, but does not start at the beginning, we include just the \n of the previous line. + let trimFirstLineFully = false; + if (lineRange.start > 0 && lineRange.endExclusive >= lines.length) { + lineRange = new OffsetRange(lineRange.start - 1, lineRange.endExclusive); + trimFirstLineFully = true; + } + + this.lineRange = lineRange; + + this.firstCharOffsetByLine[0] = 0; + for (let i = this.lineRange.start; i < this.lineRange.endExclusive; i++) { + let line = lines[i]; + let offset = 0; + if (trimFirstLineFully) { + offset = line.length; + line = ''; + trimFirstLineFully = false; + } else if (!considerWhitespaceChanges) { + const trimmedStartLine = line.trimStart(); + offset = line.length - trimmedStartLine.length; + line = trimmedStartLine.trimEnd(); + } + + this.additionalOffsetByLine.push(offset); + + for (let i = 0; i < line.length; i++) { + this.elements.push(line.charCodeAt(i)); + } + + // Don't add an \n that does not exist in the document. + if (i < lines.length - 1) { + this.elements.push('\n'.charCodeAt(0)); + this.firstCharOffsetByLine[i - this.lineRange.start + 1] = this.elements.length; + } + } + // To account for the last line + this.additionalOffsetByLine.push(0); + } + + toString() { + return `Slice: "${this.text}"`; + } + + get text(): string { + return this.getText(new OffsetRange(0, this.length)); + } + + getText(range: OffsetRange): string { + return this.elements.slice(range.start, range.endExclusive).map(e => String.fromCharCode(e)).join(''); + } + + getElement(offset: number): number { + return this.elements[offset]; + } + + get length(): number { + return this.elements.length; + } + + public getBoundaryScore(length: number): number { + // a b c , d e f + // 11 0 0 12 15 6 13 0 0 11 + + const prevCategory = getCategory(length > 0 ? this.elements[length - 1] : -1); + const nextCategory = getCategory(length < this.elements.length ? this.elements[length] : -1); + + if (prevCategory === CharBoundaryCategory.LineBreakCR && nextCategory === CharBoundaryCategory.LineBreakLF) { + // don't break between \r and \n + return 0; + } + + let score = 0; + if (prevCategory !== nextCategory) { + score += 10; + if (nextCategory === CharBoundaryCategory.WordUpper) { + score += 1; + } + } + + score += getCategoryBoundaryScore(prevCategory); + score += getCategoryBoundaryScore(nextCategory); + + return score; + } + + public translateOffset(offset: number): Position { + // find smallest i, so that lineBreakOffsets[i] <= offset using binary search + if (this.lineRange.isEmpty) { + return new Position(this.lineRange.start + 1, 1); + } + + const i = findLastIdxMonotonous(this.firstCharOffsetByLine, (value) => value <= offset); + return new Position(this.lineRange.start + i + 1, offset - this.firstCharOffsetByLine[i] + this.additionalOffsetByLine[i] + 1); + } + + public translateRange(range: OffsetRange): Range { + return Range.fromPositions(this.translateOffset(range.start), this.translateOffset(range.endExclusive)); + } + + /** + * Finds the word that contains the character at the given offset + */ + public findWordContaining(offset: number): OffsetRange | undefined { + if (offset < 0 || offset >= this.elements.length) { + return undefined; + } + + if (!isWordChar(this.elements[offset])) { + return undefined; + } + + // find start + let start = offset; + while (start > 0 && isWordChar(this.elements[start - 1])) { + start--; + } + + // find end + let end = offset; + while (end < this.elements.length && isWordChar(this.elements[end])) { + end++; + } + + return new OffsetRange(start, end); + } + + public countLinesIn(range: OffsetRange): number { + return this.translateOffset(range.endExclusive).lineNumber - this.translateOffset(range.start).lineNumber; + } + + public isStronglyEqual(offset1: number, offset2: number): boolean { + return this.elements[offset1] === this.elements[offset2]; + } + + public extendToFullLines(range: OffsetRange): OffsetRange { + const start = findLastMonotonous(this.firstCharOffsetByLine, x => x <= range.start) ?? 0; + const end = findFirstMonotonous(this.firstCharOffsetByLine, x => range.endExclusive <= x) ?? this.elements.length; + return new OffsetRange(start, end); + } +} + +function isWordChar(charCode: number): boolean { + return charCode >= CharCode.a && charCode <= CharCode.z + || charCode >= CharCode.A && charCode <= CharCode.Z + || charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9; +} + +const enum CharBoundaryCategory { + WordLower, + WordUpper, + WordNumber, + End, + Other, + Space, + LineBreakCR, + LineBreakLF, +} + +const score: Record = { + [CharBoundaryCategory.WordLower]: 0, + [CharBoundaryCategory.WordUpper]: 0, + [CharBoundaryCategory.WordNumber]: 0, + [CharBoundaryCategory.End]: 10, + [CharBoundaryCategory.Other]: 2, + [CharBoundaryCategory.Space]: 3, + [CharBoundaryCategory.LineBreakCR]: 10, + [CharBoundaryCategory.LineBreakLF]: 10, +}; + +function getCategoryBoundaryScore(category: CharBoundaryCategory): number { + return score[category]; +} + +function getCategory(charCode: number): CharBoundaryCategory { + if (charCode === CharCode.LineFeed) { + return CharBoundaryCategory.LineBreakLF; + } else if (charCode === CharCode.CarriageReturn) { + return CharBoundaryCategory.LineBreakCR; + } else if (isSpace(charCode)) { + return CharBoundaryCategory.Space; + } else if (charCode >= CharCode.a && charCode <= CharCode.z) { + return CharBoundaryCategory.WordLower; + } else if (charCode >= CharCode.A && charCode <= CharCode.Z) { + return CharBoundaryCategory.WordUpper; + } else if (charCode >= CharCode.Digit0 && charCode <= CharCode.Digit9) { + return CharBoundaryCategory.WordNumber; + } else if (charCode === -1) { + return CharBoundaryCategory.End; + } else { + return CharBoundaryCategory.Other; + } +} + diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts new file mode 100644 index 00000000000..533b71e3740 --- /dev/null +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts @@ -0,0 +1,74 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from 'vs/base/common/charCode'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; + +export class Array2D { + private readonly array: T[] = []; + + constructor(public readonly width: number, public readonly height: number) { + this.array = new Array(width * height); + } + + get(x: number, y: number): T { + return this.array[x + y * this.width]; + } + + set(x: number, y: number, value: T): void { + this.array[x + y * this.width] = value; + } +} + +export function isSpace(charCode: number): boolean { + return charCode === CharCode.Space || charCode === CharCode.Tab; +} + +export class LineRangeFragment { + private static chrKeys = new Map(); + + private static getKey(chr: string): number { + let key = this.chrKeys.get(chr); + if (key === undefined) { + key = this.chrKeys.size; + this.chrKeys.set(chr, key); + } + return key; + } + + private readonly totalCount: number; + private readonly histogram: number[] = []; + constructor( + public readonly range: LineRange, + public readonly lines: string[], + public readonly source: DetailedLineRangeMapping, + ) { + let counter = 0; + for (let i = range.startLineNumber - 1; i < range.endLineNumberExclusive - 1; i++) { + const line = lines[i]; + for (let j = 0; j < line.length; j++) { + counter++; + const chr = line[j]; + const key = LineRangeFragment.getKey(chr); + this.histogram[key] = (this.histogram[key] || 0) + 1; + } + counter++; + const key = LineRangeFragment.getKey('\n'); + this.histogram[key] = (this.histogram[key] || 0) + 1; + } + + this.totalCount = counter; + } + + public computeSimilarity(other: LineRangeFragment): number { + let sumDifferences = 0; + const maxLength = Math.max(this.histogram.length, other.histogram.length); + for (let i = 0; i < maxLength; i++) { + sumDifferences += Math.abs((this.histogram[i] ?? 0) - (other.histogram[i] ?? 0)); + } + return 1 - (sumDifferences / (this.totalCount + other.totalCount)); + } +} diff --git a/src/vs/editor/common/diff/linesDiffComputers.ts b/src/vs/editor/common/diff/linesDiffComputers.ts index 727b91455b7..75c63fe6552 100644 --- a/src/vs/editor/common/diff/linesDiffComputers.ts +++ b/src/vs/editor/common/diff/linesDiffComputers.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; -import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; +import { ILinesDiffComputer } from 'vs/editor/common/diff/linesDiffComputer'; export const linesDiffComputers = { getLegacy: () => new LegacyLinesDiffComputer(), - getAdvanced: () => new AdvancedLinesDiffComputer(), -}; + getDefault: () => new DefaultLinesDiffComputer(), +} satisfies Record ILinesDiffComputer>; diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts index 12ac2a362e9..9b69d90fbad 100644 --- a/src/vs/editor/common/diff/rangeMapping.ts +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -6,6 +6,9 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; import { Range } from 'vs/editor/common/core/range'; +/** + * Maps a line range in the original text model to a line range in the modified text model. + */ export class LineRangeMapping { public static inverse(mapping: readonly DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number): DetailedLineRangeMapping[] { const result: DetailedLineRangeMapping[] = []; @@ -76,6 +79,7 @@ export class LineRangeMapping { /** * Maps a line range in the original text model to a line range in the modified text model. + * Also contains inner range mappings. */ export class DetailedLineRangeMapping extends LineRangeMapping { /** @@ -116,7 +120,6 @@ export class RangeMapping { constructor( originalRange: Range, - modifiedRange: Range ) { this.originalRange = originalRange; diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 335e6b5c883..86ae8f966a4 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -414,7 +414,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { } private static computeDiff(originalTextModel: ICommonModel | ITextModel, modifiedTextModel: ICommonModel | ITextModel, options: IDocumentDiffProviderOptions, algorithm: DiffAlgorithmName): IDiffComputationResult { - const diffAlgorithm: ILinesDiffComputer = algorithm === 'advanced' ? linesDiffComputers.getAdvanced() : linesDiffComputers.getLegacy(); + const diffAlgorithm: ILinesDiffComputer = algorithm === 'advanced' ? linesDiffComputers.getDefault() : linesDiffComputers.getLegacy(); const originalLines = originalTextModel.getLinesContent(); const modifiedLines = modifiedTextModel.getLinesContent(); @@ -610,7 +610,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { const originalLines = original.split(/\r\n|\n|\r/); const modifiedLines = text.split(/\r\n|\n|\r/); - const diff = linesDiffComputers.getAdvanced().computeDiff(originalLines, modifiedLines, options); + const diff = linesDiffComputers.getDefault().computeDiff(originalLines, modifiedLines, options); const start = Range.lift(range).getStartPosition(); diff --git a/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts index f5e4bbacdbc..60a055761da 100644 --- a/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts +++ b/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { RangeMapping } from 'vs/editor/common/diff/rangeMapping'; -import { LinesSliceCharSequence, getLineRangeMapping } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { LinesSliceCharSequence, getLineRangeMapping } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; suite('lineRangeMapping', () => { diff --git a/src/vs/editor/test/node/diffing/diffingFixture.test.ts b/src/vs/editor/test/node/diffing/diffingFixture.test.ts index 89490813b78..bb42c69d62c 100644 --- a/src/vs/editor/test/node/diffing/diffingFixture.test.ts +++ b/src/vs/editor/test/node/diffing/diffingFixture.test.ts @@ -10,7 +10,7 @@ import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { FileAccess } from 'vs/base/common/network'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; -import { AdvancedLinesDiffComputer } from 'vs/editor/common/diff/advancedLinesDiffComputer'; +import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; suite('diff fixtures', () => { setup(() => { @@ -38,7 +38,7 @@ suite('diff fixtures', () => { const secondContent = readFileSync(join(folderPath, secondFileName), 'utf8').replaceAll('\r\n', '\n').replaceAll('\r', '\n'); const secondContentLines = secondContent.split(/\n/); - const diffingAlgo = diffingAlgoName === 'legacy' ? new LegacyLinesDiffComputer() : new AdvancedLinesDiffComputer(); + const diffingAlgo = diffingAlgoName === 'legacy' ? new LegacyLinesDiffComputer() : new DefaultLinesDiffComputer(); const ignoreTrimWhitespace = folder.indexOf('trimws') >= 0; const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }); diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 51ce58011f8..b6519772cc2 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2494,6 +2494,7 @@ declare namespace monaco.editor { /** * Maps a line range in the original text model to a line range in the modified text model. + * Also contains inner range mappings. */ export class DetailedLineRangeMapping extends LineRangeMapping { /** @@ -2524,6 +2525,9 @@ declare namespace monaco.editor { flip(): RangeMapping; } + /** + * Maps a line range in the original text model to a line range in the modified text model. + */ export class LineRangeMapping { static inverse(mapping: readonly DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number): DetailedLineRangeMapping[]; /** From 71dd6e73a04a26645550c5120187b5cdbbbb5f7f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 10:36:23 +0200 Subject: [PATCH 461/607] Fixes CI --- .../editor/test/node/diffing/advancedLinesDiffComputer.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts index 60a055761da..31fa3e3086e 100644 --- a/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts +++ b/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts @@ -6,8 +6,9 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { RangeMapping } from 'vs/editor/common/diff/rangeMapping'; -import { LinesSliceCharSequence, getLineRangeMapping } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; +import { getLineRangeMapping } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; +import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; suite('lineRangeMapping', () => { test('1', () => { From e9f7140ea818efbd343898a1dec0b7c2cf9f7b91 Mon Sep 17 00:00:00 2001 From: Lukasz Samson Date: Mon, 4 Sep 2023 11:36:49 +0200 Subject: [PATCH 462/607] Fix invalid match on `exited` DAP event DAP spec states that the event is `exited` not `exit` https://microsoft.github.io/debug-adapter-protocol/specification#Events_Exited --- src/vs/workbench/contrib/debug/browser/rawDebugSession.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index c7201795adf..de7be37fe8f 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -142,7 +142,7 @@ export class RawDebugSession implements IDisposable { case 'terminated': this._onDidTerminateDebugee.fire(event); break; - case 'exit': + case 'exited': this._onDidExitDebugee.fire(event); break; case 'progressStart': From 63d4fe776bceeaea6906f882918699d7f054b611 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 4 Sep 2023 13:30:46 +0200 Subject: [PATCH 463/607] fix #191860 (#192121) --- .../browser/extensions.contribution.ts | 10 +++++++++ test/automation/src/extensions.ts | 22 +++++-------------- test/automation/src/workbench.ts | 2 +- .../src/areas/extensions/extensions.test.ts | 3 +-- .../src/areas/workbench/localization.test.ts | 1 - 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 3d40bfa801d..ff33245ded3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -532,6 +532,16 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi order: 3 })); + this.registerExtensionAction({ + id: 'workbench.extensions.action.focusExtensionsView', + title: { value: localize('focusExtensions', "Focus on Extensions View"), original: 'Focus on Extensions View' }, + category: ExtensionsLocalizedLabel, + f1: true, + run: async (accessor: ServicesAccessor) => { + await accessor.get(IPaneCompositePartService).openPaneComposite(VIEWLET_ID, ViewContainerLocation.Sidebar, true); + } + }); + this.registerExtensionAction({ id: 'workbench.extensions.action.installExtensions', title: { value: localize('installExtensions', "Install Extensions"), original: 'Install Extensions' }, diff --git a/test/automation/src/extensions.ts b/test/automation/src/extensions.ts index 7168258d308..08bb4585f61 100644 --- a/test/automation/src/extensions.ts +++ b/test/automation/src/extensions.ts @@ -9,30 +9,18 @@ import path = require('path'); import fs = require('fs'); import { ncp } from 'ncp'; import { promisify } from 'util'; +import { Commands } from './workbench'; -const SEARCH_BOX = 'div.extensions-viewlet[id="workbench.view.extensions"] .monaco-editor textarea'; -const REFRESH_BUTTON = 'div.part.sidebar.left[id="workbench.parts.sidebar"] .codicon.codicon-extensions-refresh'; export class Extensions extends Viewlet { - constructor(code: Code) { + constructor(code: Code, private commands: Commands) { super(code); } - async openExtensionsViewlet(): Promise { - if (process.platform === 'darwin') { - await this.code.dispatchKeybinding('cmd+shift+x'); - } else { - await this.code.dispatchKeybinding('ctrl+shift+x'); - } - - await this.code.waitForActiveElement(SEARCH_BOX); - } - async searchForExtension(id: string): Promise { - await this.code.waitAndClick(SEARCH_BOX); - await this.code.waitForActiveElement(SEARCH_BOX); - await this.code.waitForTypeInEditor(SEARCH_BOX, `@id:${id}`); + await this.commands.runCommand('workbench.extensions.action.focusExtensionsView'); + await this.code.waitForTypeInEditor('div.extensions-viewlet[id="workbench.view.extensions"] .monaco-editor textarea', `@id:${id}`); await this.code.waitForTextContent(`div.part.sidebar div.composite.title h2`, 'Extensions: Marketplace'); let retrials = 1; @@ -41,7 +29,7 @@ export class Extensions extends Viewlet { return await this.code.waitForElement(`div.extensions-viewlet[id="workbench.view.extensions"] .monaco-list-row[data-extension-id="${id}"]`, undefined, 100); } catch (error) { this.code.logger.log(`Extension '${id}' is not found. Retrying count: ${retrials}`); - await this.code.waitAndClick(REFRESH_BUTTON); + await this.commands.runCommand('workbench.extensions.action.refreshExtension'); } } throw new Error(`Extension ${id} is not found`); diff --git a/test/automation/src/workbench.ts b/test/automation/src/workbench.ts index b951271d3b2..ffb628d16fd 100644 --- a/test/automation/src/workbench.ts +++ b/test/automation/src/workbench.ts @@ -55,7 +55,7 @@ export class Workbench { this.explorer = new Explorer(code); this.activitybar = new ActivityBar(code); this.search = new Search(code); - this.extensions = new Extensions(code); + this.extensions = new Extensions(code, this.quickaccess); this.editor = new Editor(code, this.quickaccess); this.scm = new SCM(code); this.debug = new Debug(code, this.quickaccess, this.editors, this.editor); diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index a8120cb12bf..c78cbe87089 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -7,7 +7,7 @@ import { Application, Logger } from '../../../../automation'; import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { - describe.skip('Extensions', () => { + describe('Extensions', () => { // Shared before/after handling installAllHandlers(logger); @@ -15,7 +15,6 @@ export function setup(logger: Logger) { it('install and enable vscode-smoketest-check extension', async function () { const app = this.app as Application; - await app.workbench.extensions.openExtensionsViewlet(); await app.workbench.extensions.installExtension('ms-vscode.vscode-smoketest-check', true); // Close extension editor because keybindings dispatch is not working when web views are opened and focused diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 4a5b62d8d7a..12e49ce549e 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -15,7 +15,6 @@ export function setup(logger: Logger) { it('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(); await app.workbench.extensions.installExtension('ms-ceintl.vscode-language-pack-de', false); await app.restart({ extraArgs: ['--locale=DE'] }); From f44e4ed8d43dfdce341599a0297e102923663eb9 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 4 Sep 2023 13:44:39 +0200 Subject: [PATCH 464/607] chore - some errors cleanup --- src/vs/base/common/errors.ts | 14 ++++---------- .../workbench/api/browser/mainThreadEditors.ts | 16 ++++++++-------- src/vs/workbench/api/common/extHost.api.impl.ts | 8 ++++---- src/vs/workbench/api/common/extHostTextEditor.ts | 8 ++++---- 4 files changed, 20 insertions(+), 26 deletions(-) diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index a558a0b06d8..f0d9296057b 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -201,16 +201,10 @@ export function illegalState(name?: string): Error { } } -export function readonly(name?: string): Error { - return name - ? new Error(`readonly property '${name} cannot be changed'`) - : new Error('readonly property cannot be changed'); -} - -export function disposed(what: string): Error { - const result = new Error(`${what} has been disposed`); - result.name = 'DISPOSED'; - return result; +export class ReadonlyError extends TypeError { + constructor(name?: string) { + super(name ? `${name} is read-only and cannot be changed` : 'Cannot change read-only property'); + } } export function getErrorMessage(err: any): string { diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index b5e8bb0ef56..84bc6f34f1e 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { disposed } from 'vs/base/common/errors'; +import { illegalArgument } from 'vs/base/common/errors'; import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle'; import { equals as objectEquals } from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; @@ -174,7 +174,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $trySetSelections(id: string, selections: ISelection[]): Promise { const editor = this._editorLocator.getEditor(id); if (!editor) { - return Promise.reject(disposed(`TextEditor(${id})`)); + return Promise.reject(illegalArgument(`TextEditor(${id})`)); } editor.setSelections(selections); return Promise.resolve(undefined); @@ -184,7 +184,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { key = `${this._instanceId}-${key}`; const editor = this._editorLocator.getEditor(id); if (!editor) { - return Promise.reject(disposed(`TextEditor(${id})`)); + return Promise.reject(illegalArgument(`TextEditor(${id})`)); } editor.setDecorations(key, ranges); return Promise.resolve(undefined); @@ -194,7 +194,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { key = `${this._instanceId}-${key}`; const editor = this._editorLocator.getEditor(id); if (!editor) { - return Promise.reject(disposed(`TextEditor(${id})`)); + return Promise.reject(illegalArgument(`TextEditor(${id})`)); } editor.setDecorationsFast(key, ranges); return Promise.resolve(undefined); @@ -203,7 +203,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $tryRevealRange(id: string, range: IRange, revealType: TextEditorRevealType): Promise { const editor = this._editorLocator.getEditor(id); if (!editor) { - return Promise.reject(disposed(`TextEditor(${id})`)); + return Promise.reject(illegalArgument(`TextEditor(${id})`)); } editor.revealRange(range, revealType); return Promise.resolve(); @@ -212,7 +212,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $trySetOptions(id: string, options: ITextEditorConfigurationUpdate): Promise { const editor = this._editorLocator.getEditor(id); if (!editor) { - return Promise.reject(disposed(`TextEditor(${id})`)); + return Promise.reject(illegalArgument(`TextEditor(${id})`)); } editor.setConfiguration(options); return Promise.resolve(undefined); @@ -221,7 +221,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $tryApplyEdits(id: string, modelVersionId: number, edits: ISingleEditOperation[], opts: IApplyEditsOptions): Promise { const editor = this._editorLocator.getEditor(id); if (!editor) { - return Promise.reject(disposed(`TextEditor(${id})`)); + return Promise.reject(illegalArgument(`TextEditor(${id})`)); } return Promise.resolve(editor.applyEdits(modelVersionId, edits, opts)); } @@ -229,7 +229,7 @@ export class MainThreadTextEditors implements MainThreadTextEditorsShape { $tryInsertSnippet(id: string, modelVersionId: number, template: string, ranges: readonly IRange[], opts: IUndoStopOptions): Promise { const editor = this._editorLocator.getEditor(id); if (!editor) { - return Promise.reject(disposed(`TextEditor(${id})`)); + return Promise.reject(illegalArgument(`TextEditor(${id})`)); } return Promise.resolve(editor.insertSnippet(modelVersionId, template, ranges, opts)); } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 5eadd12f6dd..95d016287a2 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -876,7 +876,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWorkspace.getPath(); }, set rootPath(value) { - throw errors.readonly(); + throw new errors.ReadonlyError('rootPath'); }, getWorkspaceFolder(resource) { return extHostWorkspace.getWorkspaceFolder(resource); @@ -888,13 +888,13 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWorkspace.name; }, set name(value) { - throw errors.readonly(); + throw new errors.ReadonlyError('name'); }, get workspaceFile() { return extHostWorkspace.workspaceFile; }, set workspaceFile(value) { - throw errors.readonly(); + throw new errors.ReadonlyError('workspaceFile'); }, updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) => { return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount || 0, ...workspaceFoldersToAdd); @@ -946,7 +946,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostDocuments.getAllDocumentData().map(data => data.document); }, set textDocuments(value) { - throw errors.readonly(); + throw new errors.ReadonlyError('textDocuments'); }, openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string }) { let uriPromise: Thenable; diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index c1317c709d9..8ab93b18b21 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ok } from 'vs/base/common/assert'; -import { illegalArgument, readonly } from 'vs/base/common/errors'; +import { ReadonlyError, illegalArgument } from 'vs/base/common/errors'; import { IdGenerator } from 'vs/base/common/idGenerator'; import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { IRange } from 'vs/editor/common/core/range'; @@ -431,7 +431,7 @@ export class ExtHostTextEditor { return document.value; }, set document(_value) { - throw readonly('document'); + throw new ReadonlyError('document'); }, // --- selection get selection(): Selection { @@ -459,7 +459,7 @@ export class ExtHostTextEditor { return that._visibleRanges; }, set visibleRanges(_value: Range[]) { - throw readonly('visibleRanges'); + throw new ReadonlyError('visibleRanges'); }, // --- options get options(): vscode.TextEditorOptions { @@ -475,7 +475,7 @@ export class ExtHostTextEditor { return that._viewColumn; }, set viewColumn(_value) { - throw readonly('viewColumn'); + throw new ReadonlyError('viewColumn'); }, // --- edit edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean } = { undoStopBefore: true, undoStopAfter: true }): Promise { From 1ac6f50f44afc9073b25a1264fda084b0d434983 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 4 Sep 2023 14:36:16 +0200 Subject: [PATCH 465/607] Enable the `..` argument for git log (#188500) * Enable the `..` argument for git log This will return the commits that the `toRef` has but the `fromRef` does not. * Use range instead --- extensions/git/src/api/git.d.ts | 2 ++ extensions/git/src/git.ts | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index ae1d57d3098..05af77899f0 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -129,6 +129,8 @@ export interface LogOptions { /** Max number of log entries to retrieve. If not specified, the default is 32. */ readonly maxEntries?: number; readonly path?: string; + /** A commit range, such as "0a47c67f0fb52dd11562af48658bc1dff1d75a38..0bb4bdea78e1db44d728fd6894720071e303304f" */ + readonly range?: string; } export interface CommitOptions { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 65d34af1e31..36dbac3a56e 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1022,7 +1022,14 @@ export class Repository { async log(options?: LogOptions): Promise { const maxEntries = options?.maxEntries ?? 32; - const args = ['log', `-n${maxEntries}`, `--format=${COMMIT_FORMAT}`, '-z', '--']; + const args = ['log', `-n${maxEntries}`, `--format=${COMMIT_FORMAT}`, '-z']; + + if (options?.range) { + args.push(options.range); + } + + args.push('--'); + if (options?.path) { args.push(options.path); } From 9dd556a9e06a6f9b5d7e734fea9ec00d34071a63 Mon Sep 17 00:00:00 2001 From: justanotheranonymoususer Date: Mon, 4 Sep 2023 15:44:03 +0300 Subject: [PATCH 466/607] Remove superfluous arg in git smoke.test.ts (#173194) --- extensions/git/src/test/smoke.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/test/smoke.test.ts b/extensions/git/src/test/smoke.test.ts index 789086e90a4..b0070eb127f 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -122,7 +122,7 @@ suite('git smoke test', function () { repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED); assert.strictEqual(repository.state.indexChanges.length, 0); - await commands.executeCommand('git.stageAll', appjs); + await commands.executeCommand('git.stageAll'); await repository.commit('third commit'); assert.strictEqual(repository.state.workingTreeChanges.length, 0); assert.strictEqual(repository.state.indexChanges.length, 0); From b481d52f179649efd19dc36199f150b3cd85f6eb Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 14:36:53 +0200 Subject: [PATCH 467/607] Introduces ownership model to observable for better debug messages. --- src/vs/base/common/observableInternal/base.ts | 99 +++++++++++++++---- .../base/common/observableInternal/derived.ts | 73 ++++++++++---- .../base/common/observableInternal/utils.ts | 31 +++--- .../diffEditorWidget2/accessibleDiffViewer.ts | 8 +- .../diffEditorDecorations.ts | 3 +- .../diffEditorWidget2/diffEditorOptions.ts | 49 +++++---- .../diffEditorWidget2/diffEditorSash.ts | 5 +- .../diffEditorWidget2/diffEditorViewModel.ts | 25 +++-- .../diffEditorWidget2/diffEditorWidget2.ts | 15 ++- .../hideUnchangedRegionsFeature.ts | 7 +- .../widget/diffEditorWidget2/lineAlignment.ts | 8 +- .../diffEditorWidget2/movedBlocksLines.ts | 6 +- .../browser/widget/diffEditorWidget2/utils.ts | 10 +- .../browser/ghostTextWidget.ts | 8 +- .../browser/inlineCompletionsController.ts | 4 +- .../browser/inlineCompletionsHintsWidget.ts | 3 +- .../browser/inlineCompletionsModel.ts | 24 +++-- .../browser/inlineCompletionsSource.ts | 3 +- .../suggestWidgetInlineCompletionProvider.ts | 2 +- .../contrib/debug/common/debugStorage.ts | 10 +- .../browser/mergeEditorInputModel.ts | 6 +- .../browser/model/mergeEditorModel.ts | 48 ++++----- .../browser/model/textModelDiffs.ts | 4 +- .../browser/view/conflictActions.ts | 9 +- .../view/editors/baseCodeEditorView.ts | 3 +- .../view/editors/inputCodeEditorView.ts | 10 +- .../view/editors/resultCodeEditorView.ts | 3 +- .../mergeEditor/browser/view/mergeEditor.ts | 12 +-- .../mergeEditor/browser/view/viewModel.ts | 12 +-- .../worker/textMateWorkerTokenizer.ts | 2 +- 30 files changed, 287 insertions(+), 215 deletions(-) diff --git a/src/vs/base/common/observableInternal/base.ts b/src/vs/base/common/observableInternal/base.ts index 23ba8d4af19..31e345a0bf2 100644 --- a/src/vs/base/common/observableInternal/base.ts +++ b/src/vs/base/common/observableInternal/base.ts @@ -4,11 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from 'vs/base/common/lifecycle'; -import type { derived } from 'vs/base/common/observableInternal/derived'; +import type { derivedOpts } from 'vs/base/common/observableInternal/derived'; import { getLogger } from 'vs/base/common/observableInternal/logging'; /** * Represents an observable value. + * * @template T The type of the value. * @template TChange The type of delta information (usually `void` and only used in advanced scenarios). */ @@ -118,6 +119,10 @@ export interface IObserver { } export interface ISettable { + /** + * Sets the value of the observable. + * Use a transaction to batch multiple changes (with a transaction, observers only react at the end of the transaction). + */ set(value: T, transaction: ITransaction | undefined, change: TChange): void; } @@ -129,12 +134,12 @@ export interface ITransaction { updateObserver(observer: IObserver, observable: IObservable): void; } -let _derived: typeof derived; +let _derived: typeof derivedOpts; /** * @internal * This is to allow splitting files. */ -export function _setDerived(derived: typeof _derived) { +export function _setDerivedOpts(derived: typeof _derived) { _derived = derived; } @@ -162,21 +167,23 @@ export abstract class ConvenientObservable implements IObservable(fn: (value: T, reader: IReader) => TNew): IObservable { return _derived( - (reader) => fn(this.read(reader), reader), - () => { - const name = getFunctionName(fn); - if (name !== undefined) { - return name; - } + { + debugName: () => { + const name = getFunctionName(fn); + if (name !== undefined) { + return name; + } - // regexp to match `x => x.y` where x and y can be arbitrary identifiers (uses backref): - const regexp = /^\s*\(?\s*([a-zA-Z_$][a-zA-Z_$0-9]*)\s*\)?\s*=>\s*\1\.([a-zA-Z_$][a-zA-Z_$0-9]*)\s*$/; - const match = regexp.exec(fn.toString()); - if (match) { - return `${this.debugName}.${match[2]}`; - } - return `${this.debugName} (mapped)`; + // regexp to match `x => x.y` where x and y can be arbitrary identifiers (uses backref): + const regexp = /^\s*\(?\s*([a-zA-Z_$][a-zA-Z_$0-9]*)\s*\)?\s*=>\s*\1\.([a-zA-Z_$][a-zA-Z_$0-9]*)\s*$/; + const match = regexp.exec(fn.toString()); + if (match) { + return `${this.debugName}.${match[2]}`; + } + return `${this.debugName} (mapped)`; + }, }, + (reader) => fn(this.read(reader), reader), ); } @@ -252,6 +259,38 @@ export class TransactionImpl implements ITransaction { } } +export type DebugNameFn = string | (() => string | undefined); + +export function getDebugName(debugNameFn: DebugNameFn | undefined, fn: Function | undefined, owner: object | undefined, self: object): string | undefined { + let result: string | undefined; + if (debugNameFn !== undefined) { + if (typeof debugNameFn === 'function') { + result = debugNameFn(); + if (result !== undefined) { + return result; + } + } else { + return debugNameFn; + } + } + + if (fn !== undefined) { + result = getFunctionName(fn); + if (result !== undefined) { + return result; + } + } + + if (owner !== undefined) { + for (const key in owner) { + if ((owner as any)[key] === self) { + return key; + } + } + } + return undefined; +} + export function getFunctionName(fn: Function): string | undefined { const fnSrc = fn.toString(); // Pattern: /** @description ... */ @@ -268,8 +307,14 @@ export interface ISettableObservable extends IObservable(name: string, initialValue: T): ISettableObservable { - return new ObservableValue(name, initialValue); +export function observableValue(name: string, initialValue: T): ISettableObservable; +export function observableValue(owner: object, initialValue: T): ISettableObservable; +export function observableValue(nameOrOwner: string | object, initialValue: T): ISettableObservable { + if (typeof nameOrOwner === 'string') { + return new ObservableValue(undefined, nameOrOwner, initialValue); + } else { + return new ObservableValue(nameOrOwner, undefined, initialValue); + } } export class ObservableValue @@ -278,7 +323,15 @@ export class ObservableValue { protected _value: T; - constructor(public readonly debugName: string, initialValue: T) { + get debugName() { + return getDebugName(this._debugName, undefined, this._owner, this) ?? 'ObservableValue'; + } + + constructor( + private readonly _owner: object | undefined, + private readonly _debugName: string | undefined, + initialValue: T + ) { super(); this._value = initialValue; } @@ -320,8 +373,12 @@ export class ObservableValue } } -export function disposableObservableValue(name: string, initialValue: T): ISettableObservable & IDisposable { - return new DisposableObservableValue(name, initialValue); +export function disposableObservableValue(nameOrOwner: string | object, initialValue: T): ISettableObservable & IDisposable { + if (typeof nameOrOwner === 'string') { + return new DisposableObservableValue(undefined, nameOrOwner, initialValue); + } else { + return new DisposableObservableValue(nameOrOwner, undefined, initialValue); + } } export class DisposableObservableValue extends ObservableValue implements IDisposable { diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index 2cb17f2f9da..0f1abf6043e 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -5,39 +5,76 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IReader, IObservable, BaseObservable, IObserver, _setDerived, IChangeContext, getFunctionName } from 'vs/base/common/observableInternal/base'; +import { IReader, IObservable, BaseObservable, IObserver, _setDerivedOpts, IChangeContext, getFunctionName, DebugNameFn, getDebugName } from 'vs/base/common/observableInternal/base'; import { getLogger } from 'vs/base/common/observableInternal/logging'; export type EqualityComparer = (a: T, b: T) => boolean; const defaultEqualityComparer: EqualityComparer = (a, b) => a === b; -export function derived(computeFn: (reader: IReader) => T, debugName?: string | (() => string)): IObservable { - return new Derived(debugName, computeFn, undefined, undefined, undefined, defaultEqualityComparer); +/** + * Creates an observable that is derived from other observables. + */ +export function derived(computeFn: (reader: IReader) => T): IObservable; +export function derived(owner: object, computeFn: (reader: IReader) => T): IObservable; +export function derived(computeFnOrOwner: ((reader: IReader) => T) | object, computeFn?: ((reader: IReader) => T) | undefined): IObservable { + if (computeFn !== undefined) { + return new Derived(computeFnOrOwner, undefined, computeFn, undefined, undefined, undefined, defaultEqualityComparer); + } + return new Derived(undefined, undefined, computeFnOrOwner as any, undefined, undefined, undefined, defaultEqualityComparer); } -export function derivedOpts(options: { debugName?: string | (() => string); equalityComparer?: EqualityComparer }, computeFn: (reader: IReader) => T): IObservable { - return new Derived(options.debugName, computeFn, undefined, undefined, undefined, options.equalityComparer ?? defaultEqualityComparer); +export function derivedOpts( + options: { + owner?: object; + debugName?: string | (() => string); + equalityComparer?: EqualityComparer; + }, + computeFn: (reader: IReader) => T +): IObservable { + return new Derived(options.owner, options.debugName, computeFn, undefined, undefined, undefined, options.equalityComparer ?? defaultEqualityComparer); } export function derivedHandleChanges( - debugName: string | (() => string), options: { + owner?: object; + debugName?: string | (() => string); createEmptyChangeSummary: () => TChangeSummary; handleChange: (context: IChangeContext, changeSummary: TChangeSummary) => boolean; + equalityComparer?: EqualityComparer; }, - computeFn: (reader: IReader, changeSummary: TChangeSummary) => T): IObservable { - return new Derived(debugName, computeFn, options.createEmptyChangeSummary, options.handleChange, undefined, defaultEqualityComparer); + computeFn: (reader: IReader, changeSummary: TChangeSummary) => T +): IObservable { + return new Derived(options.owner, options.debugName, computeFn, options.createEmptyChangeSummary, options.handleChange, undefined, options.equalityComparer ?? defaultEqualityComparer); } -export function derivedWithStore(name: string, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable { +export function derivedWithStore(computeFn: (reader: IReader, store: DisposableStore) => T): IObservable; +export function derivedWithStore(owner: object, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable; +export function derivedWithStore(computeFnOrOwner: ((reader: IReader, store: DisposableStore) => T) | object, computeFnOrUndefined?: ((reader: IReader, store: DisposableStore) => T)): IObservable { + let computeFn: (reader: IReader, store: DisposableStore) => T; + let owner: object | undefined; + if (computeFnOrUndefined === undefined) { + computeFn = computeFnOrOwner as any; + owner = undefined; + } else { + owner = computeFnOrOwner; + computeFn = computeFnOrUndefined as any; + } + const store = new DisposableStore(); - return new Derived(name, r => { - store.clear(); - return computeFn(r, store); - }, undefined, undefined, () => store.dispose(), defaultEqualityComparer); + return new Derived( + owner, + (() => getFunctionName(computeFn) ?? '(anonymous)'), + r => { + store.clear(); + return computeFn(r, store); + }, undefined, + undefined, + () => store.dispose(), + defaultEqualityComparer + ); } -_setDerived(derived); +_setDerivedOpts(derived); const enum DerivedState { /** Initial state, no previous value, recomputation needed */ @@ -70,14 +107,12 @@ export class Derived extends BaseObservable im private changeSummary: TChangeSummary | undefined = undefined; public override get debugName(): string { - if (!this._debugName) { - return getFunctionName(this._computeFn) || '(anonymous)'; - } - return typeof this._debugName === 'function' ? this._debugName() : this._debugName; + return getDebugName(this._debugName, this._computeFn, this._owner, this) ?? '(anonymous)'; } constructor( - private readonly _debugName: string | (() => string) | undefined, + private readonly _owner: object | undefined, + private readonly _debugName: DebugNameFn | undefined, public readonly _computeFn: (reader: IReader, changeSummary: TChangeSummary) => T, private readonly createChangeSummary: (() => TChangeSummary) | undefined, private readonly _handleChange: ((context: IChangeContext, summary: TChangeSummary) => boolean) | undefined, diff --git a/src/vs/base/common/observableInternal/utils.ts b/src/vs/base/common/observableInternal/utils.ts index c0e0adba010..8717248dee9 100644 --- a/src/vs/base/common/observableInternal/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { autorun } from 'vs/base/common/observableInternal/autorun'; -import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, getFunctionName, observableValue, transaction } from 'vs/base/common/observableInternal/base'; +import { BaseObservable, ConvenientObservable, IObservable, IObserver, IReader, ITransaction, getDebugName, getFunctionName, observableValue, transaction } from 'vs/base/common/observableInternal/base'; import { derived } from 'vs/base/common/observableInternal/derived'; import { getLogger } from 'vs/base/common/observableInternal/logging'; @@ -207,10 +207,14 @@ class FromEventObservableSignal extends BaseObservable { * Signals don't have a value - when they are triggered they indicate a change. * However, signals can carry a delta that is passed to observers. */ -export function observableSignal( - debugName: string -): IObservableSignal { - return new ObservableSignal(debugName); +export function observableSignal(debugName: string): IObservableSignal; +export function observableSignal(owner: object): IObservableSignal; +export function observableSignal(debugNameOrOwner: string | object): IObservableSignal { + if (typeof debugNameOrOwner === 'string') { + return new ObservableSignal(debugNameOrOwner); + } else { + return new ObservableSignal(undefined, debugNameOrOwner); + } } export interface IObservableSignal extends IObservable { @@ -218,8 +222,13 @@ export interface IObservableSignal extends IObservable { } class ObservableSignal extends BaseObservable implements IObservableSignal { + public get debugName() { + return getDebugName(this._debugName, undefined, this._owner, this) ?? 'Observable Signal'; + } + constructor( - public readonly debugName: string + private readonly _debugName: string | undefined, + private readonly _owner?: object, ) { super(); } @@ -332,23 +341,23 @@ class KeepAliveObserver implements IObserver { } } -export function derivedObservableWithCache(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable { +export function derivedObservableWithCache(computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable { let lastValue: T | undefined = undefined; const observable = derived(reader => { lastValue = computeFn(reader, lastValue); return lastValue; - }, name); + }); return observable; } -export function derivedObservableWithWritableCache(name: string, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable & { clearCache(transaction: ITransaction): void } { +export function derivedObservableWithWritableCache(owner: object, computeFn: (reader: IReader, lastValue: T | undefined) => T): IObservable & { clearCache(transaction: ITransaction): void } { let lastValue: T | undefined = undefined; const counter = observableValue('derivedObservableWithWritableCache.counter', 0); - const observable = derived(reader => { + const observable = derived(owner, reader => { counter.read(reader); lastValue = computeFn(reader, lastValue); return lastValue; - }, name); + }); return Object.assign(observable, { clearCache: (transaction: ITransaction) => { lastValue = undefined; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts index 5bb6c97f1e4..29022fd8bf1 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts @@ -54,7 +54,7 @@ export class AccessibleDiffViewer extends Disposable { this._register(keepAlive(this.model, true)); } - private readonly model = derivedWithStore('model', (reader, store) => { + private readonly model = derivedWithStore(this, (reader, store) => { const visible = this._visible.read(reader); this._parentNode.style.visibility = visible ? 'visible' : 'hidden'; if (!visible) { @@ -93,9 +93,9 @@ export class AccessibleDiffViewer extends Disposable { } class ViewModel extends Disposable { - private readonly _groups = observableValue('groups', []); - private readonly _currentGroupIdx = observableValue('currentGroupIdx', 0); - private readonly _currentElementIdx = observableValue('currentElementIdx', 0); + private readonly _groups = observableValue(this, []); + private readonly _currentGroupIdx = observableValue(this, 0); + private readonly _currentElementIdx = observableValue(this, 0); public readonly groups: IObservable = this._groups; public readonly currentGroup: IObservable diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts index 504bfe8a12c..bdc8100df4a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts @@ -27,8 +27,7 @@ export class DiffEditorDecorations extends Disposable { this._register(applyObservableDecorations(this._editors.modified, this._decorations.map(d => d?.modifiedDecorations || []))); } - private readonly _decorations = derived((reader) => { - /** @description _decorations */ + private readonly _decorations = derived(this, (reader) => { const diff = this._diffModel.read(reader)?.diff.read(reader); if (!diff) { return null; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts index 066eefdb1e5..ed0de92dad0 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts @@ -17,44 +17,43 @@ export class DiffEditorOptions { constructor(options: Readonly, private readonly diffEditorWidth: IObservable) { const optionsCopy = { ...options, ...validateDiffEditorOptions(options, diffEditorDefaultOptions) }; - this._options = observableValue('options', optionsCopy); + this._options = observableValue(this, optionsCopy); } - public readonly couldShowInlineViewBecauseOfSize = derived(reader => /** @description couldShowInlineViewBecauseOfSize */ this._options.read(reader).renderSideBySide && this.diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint + public readonly couldShowInlineViewBecauseOfSize = derived(this, reader => this._options.read(reader).renderSideBySide && this.diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint ); - public readonly renderOverviewRuler = derived(reader => /** @description renderOverviewRuler */ this._options.read(reader).renderOverviewRuler); - public readonly renderSideBySide = derived(reader => /** @description renderSideBySide */ this._options.read(reader).renderSideBySide + public readonly renderOverviewRuler = derived(this, reader => this._options.read(reader).renderOverviewRuler); + public readonly renderSideBySide = derived(this, reader => this._options.read(reader).renderSideBySide && !(this._options.read(reader).useInlineViewWhenSpaceIsLimited && this.couldShowInlineViewBecauseOfSize.read(reader)) ); - public readonly readOnly = derived(reader => /** @description readOnly */ this._options.read(reader).readOnly); + public readonly readOnly = derived(this, reader => this._options.read(reader).readOnly); - public readonly shouldRenderRevertArrows = derived(reader => { - /** @description shouldRenderRevertArrows */ + public readonly shouldRenderRevertArrows = derived(this, reader => { if (!this._options.read(reader).renderMarginRevertIcon) { return false; } if (!this.renderSideBySide.read(reader)) { return false; } if (this.readOnly.read(reader)) { return false; } return true; }); - public readonly renderIndicators = derived(reader => /** @description renderIndicators */ this._options.read(reader).renderIndicators); - public readonly enableSplitViewResizing = derived(reader => /** @description enableSplitViewResizing */ this._options.read(reader).enableSplitViewResizing); - public readonly splitViewDefaultRatio = derived(reader => /** @description splitViewDefaultRatio */ this._options.read(reader).splitViewDefaultRatio); - public readonly ignoreTrimWhitespace = derived(reader => /** @description ignoreTrimWhitespace */ this._options.read(reader).ignoreTrimWhitespace); - public readonly maxComputationTimeMs = derived(reader => /** @description maxComputationTime */ this._options.read(reader).maxComputationTime); - public readonly showMoves = derived(reader => /** @description showMoves */ this._options.read(reader).experimental.showMoves! && this.renderSideBySide.read(reader)); - public readonly isInEmbeddedEditor = derived(reader => /** @description isInEmbeddedEditor */ this._options.read(reader).isInEmbeddedEditor); - public readonly diffWordWrap = derived(reader => /** @description diffWordWrap */ this._options.read(reader).diffWordWrap); - public readonly originalEditable = derived(reader => /** @description originalEditable */ this._options.read(reader).originalEditable); - public readonly diffCodeLens = derived(reader => /** @description diffCodeLens */ this._options.read(reader).diffCodeLens); - public readonly accessibilityVerbose = derived(reader => /** @description accessibilityVerbose */ this._options.read(reader).accessibilityVerbose); - public readonly diffAlgorithm = derived(reader => /** @description diffAlgorithm */ this._options.read(reader).diffAlgorithm); - public readonly showEmptyDecorations = derived(reader => /** @description showEmptyDecorations */ this._options.read(reader).experimental.showEmptyDecorations!); - public readonly onlyShowAccessibleDiffViewer = derived(reader => /** @description onlyShowAccessibleDiffViewer */ this._options.read(reader).onlyShowAccessibleDiffViewer); + public readonly renderIndicators = derived(this, reader => this._options.read(reader).renderIndicators); + public readonly enableSplitViewResizing = derived(this, reader => this._options.read(reader).enableSplitViewResizing); + public readonly splitViewDefaultRatio = derived(this, reader => this._options.read(reader).splitViewDefaultRatio); + public readonly ignoreTrimWhitespace = derived(this, reader => this._options.read(reader).ignoreTrimWhitespace); + public readonly maxComputationTimeMs = derived(this, reader => this._options.read(reader).maxComputationTime); + public readonly showMoves = derived(this, reader => this._options.read(reader).experimental.showMoves! && this.renderSideBySide.read(reader)); + public readonly isInEmbeddedEditor = derived(this, reader => this._options.read(reader).isInEmbeddedEditor); + public readonly diffWordWrap = derived(this, reader => this._options.read(reader).diffWordWrap); + public readonly originalEditable = derived(this, reader => this._options.read(reader).originalEditable); + public readonly diffCodeLens = derived(this, reader => this._options.read(reader).diffCodeLens); + public readonly accessibilityVerbose = derived(this, reader => this._options.read(reader).accessibilityVerbose); + public readonly diffAlgorithm = derived(this, reader => this._options.read(reader).diffAlgorithm); + public readonly showEmptyDecorations = derived(this, reader => this._options.read(reader).experimental.showEmptyDecorations!); + public readonly onlyShowAccessibleDiffViewer = derived(this, reader => this._options.read(reader).onlyShowAccessibleDiffViewer); - public readonly hideUnchangedRegions = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.enabled!); - public readonly hideUnchangedRegionsRevealLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.revealLineCount!); - public readonly hideUnchangedRegionsContextLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.contextLineCount!); - public readonly hideUnchangedRegionsMinimumLineCount = derived(reader => /** @description hideUnchangedRegions */ this._options.read(reader).hideUnchangedRegions.minimumLineCount!); + public readonly hideUnchangedRegions = derived(this, reader => this._options.read(reader).hideUnchangedRegions.enabled!); + public readonly hideUnchangedRegionsRevealLineCount = derived(this, reader => this._options.read(reader).hideUnchangedRegions.revealLineCount!); + public readonly hideUnchangedRegionsContextLineCount = derived(this, reader => this._options.read(reader).hideUnchangedRegions.contextLineCount!); + public readonly hideUnchangedRegionsMinimumLineCount = derived(this, reader => this._options.read(reader).hideUnchangedRegions.minimumLineCount!); public updateOptions(changedOptions: IDiffEditorOptions): void { const newDiffEditorOptions = validateDiffEditorOptions(changedOptions, this._options.get()); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts index aa25a92dacf..3e6664ff846 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts @@ -9,10 +9,9 @@ import { IObservable, IReader, autorun, derived, observableValue } from 'vs/base import { DiffEditorOptions } from './diffEditorOptions'; export class DiffEditorSash extends Disposable { - private readonly _sashRatio = observableValue('sashRatio', undefined); + private readonly _sashRatio = observableValue(this, undefined); - public readonly sashLeft = derived(reader => { - /** @description sashLeft */ + public readonly sashLeft = derived(this, reader => { const ratio = this._sashRatio.read(reader) ?? this._options.splitViewDefaultRatio.read(reader); return this._computeSashLeft(ratio, reader); }); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 30697f69a85..60bd6f8cba8 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -20,19 +20,18 @@ import { combineTextEditInfos } from 'vs/editor/common/model/bracketPairsTextMod import { DiffEditorOptions } from './diffEditorOptions'; export class DiffEditorViewModel extends Disposable implements IDiffEditorViewModel { - private readonly _isDiffUpToDate = observableValue('isDiffUpToDate', false); + private readonly _isDiffUpToDate = observableValue(this, false); public readonly isDiffUpToDate: IObservable = this._isDiffUpToDate; private _lastDiff: IDocumentDiff | undefined; - private readonly _diff = observableValue('diff', undefined); + private readonly _diff = observableValue(this, undefined); public readonly diff: IObservable = this._diff; private readonly _unchangedRegions = observableValue<{ regions: UnchangedRegion[]; originalDecorationIds: string[]; modifiedDecorationIds: string[] }>( - 'unchangedRegion', + this, { regions: [], originalDecorationIds: [], modifiedDecorationIds: [] } ); - public readonly unchangedRegions: IObservable = derived(r => { - /** @description unchangedRegions */ + public readonly unchangedRegions: IObservable = derived(this, r => { if (this._options.hideUnchangedRegions.read(r)) { return this._unchangedRegions.read(r).regions; } else { @@ -47,13 +46,13 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo } ); - public readonly movedTextToCompare = observableValue('movedTextToCompare', undefined); + public readonly movedTextToCompare = observableValue(this, undefined); - private readonly _activeMovedText = observableValue('activeMovedText', undefined); - private readonly _hoveredMovedText = observableValue('hoveredMovedText', undefined); + private readonly _activeMovedText = observableValue(this, undefined); + private readonly _hoveredMovedText = observableValue(this, undefined); - public readonly activeMovedText = derived(r => this.movedTextToCompare.read(r) ?? this._hoveredMovedText.read(r) ?? this._activeMovedText.read(r)); + public readonly activeMovedText = derived(this, r => this.movedTextToCompare.read(r) ?? this._hoveredMovedText.read(r) ?? this._activeMovedText.read(r)); public setActiveMovedText(movedText: MovedText | undefined): void { this._activeMovedText.set(movedText, undefined); @@ -365,16 +364,16 @@ export class UnchangedRegion { return LineRange.ofLength(this.modifiedLineNumber, this.lineCount); } - private readonly _visibleLineCountTop = observableValue('visibleLineCountTop', 0); + private readonly _visibleLineCountTop = observableValue(this, 0); public readonly visibleLineCountTop: ISettableObservable = this._visibleLineCountTop; - private readonly _visibleLineCountBottom = observableValue('visibleLineCountBottom', 0); + private readonly _visibleLineCountBottom = observableValue(this, 0); public readonly visibleLineCountBottom: ISettableObservable = this._visibleLineCountBottom; - private readonly _shouldHideControls = derived(reader => /** @description isVisible */ + private readonly _shouldHideControls = derived(this, reader => /** @description isVisible */ this.visibleLineCountTop.read(reader) + this.visibleLineCountBottom.read(reader) === this.lineCount && !this.isDragged.read(reader)); - public readonly isDragged = observableValue('isDragged', false); + public readonly isDragged = observableValue(this, false); constructor( public readonly originalLineNumber: number, diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 40094652891..bd99533b577 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -66,13 +66,13 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly _rootSizeObserver: ObservableElementSizeObserver; private readonly _sash: IObservable; - private readonly _boundarySashes = observableValue('boundarySashes', undefined); + private readonly _boundarySashes = observableValue(this, undefined); private unchangedRangesFeature!: HideUnchangedRegionsFeature; - private _accessibleDiffViewerShouldBeVisible = observableValue('accessibleDiffViewerShouldBeVisible', false); - private _accessibleDiffViewerVisible = derived(reader => - /** @description accessibleDiffViewerVisible */ this._options.onlyShowAccessibleDiffViewer.read(reader) + private _accessibleDiffViewerShouldBeVisible = observableValue(this, false); + private _accessibleDiffViewerVisible = derived(this, reader => + this._options.onlyShowAccessibleDiffViewer.read(reader) ? true : this._accessibleDiffViewerShouldBeVisible.read(reader) ); @@ -80,7 +80,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly _options: DiffEditorOptions; private readonly _editors: DiffEditorEditors; - private readonly movedBlocksLinesPart = observableValue('MovedBlocksLinesPart', undefined); + private readonly movedBlocksLinesPart = observableValue(this, undefined); public get collapseUnchangedRegions() { return this._options.hideUnchangedRegions.get(); } @@ -136,7 +136,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { (i, c, o, o2) => this._createInnerEditor(i, c, o, o2) )); - this._sash = derivedWithStore('sash', (reader, store) => { + this._sash = derivedWithStore(this, (reader, store) => { const showSash = this._options.renderSideBySide.read(reader); this.elements.root.classList.toggle('side-by-side', showSash); if (!showSash) { return undefined; } @@ -289,8 +289,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { return editor; } - private readonly _layoutInfo = derived(reader => { - /** @description modifiedEditorLayoutInfo */ + private readonly _layoutInfo = derived(this, reader => { const width = this._rootSizeObserver.width.read(reader); const height = this._rootSizeObserver.height.read(reader); const sashLeft = this._sash.read(reader)?.sashLeft.read(reader); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts index 6c7851b9416..5beb8c3ff37 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts @@ -33,7 +33,7 @@ export class HideUnchangedRegionsFeature extends Disposable { private _isUpdatingViewZones = false; public get isUpdatingViewZones(): boolean { return this._isUpdatingViewZones; } - private readonly _modifiedOutlineSource = derivedWithStore('modified outline source', (reader, store) => { + private readonly _modifiedOutlineSource = derivedWithStore(this, (reader, store) => { const m = this._editors.modifiedModel.read(reader); if (!m) { return undefined; } return store.add(new OutlineSource(this._languageFeaturesService, m)); @@ -73,7 +73,8 @@ export class HideUnchangedRegionsFeature extends Disposable { const unchangedRegions = this._diffModel.map((m, reader) => m?.diff.read(reader)?.mappings.length === 0 ? [] : m?.unchangedRegions.read(reader) ?? []); - const viewZones = derivedWithStore('view zones', (reader, store) => { + const viewZones = derivedWithStore(this, (reader, store) => { + /** @description view Zones */ const modifiedOutlineSource = this._modifiedOutlineSource.read(reader); if (!modifiedOutlineSource) { return { origViewZones: [], modViewZones: [] }; } @@ -210,7 +211,7 @@ export class HideUnchangedRegionsFeature extends Disposable { } class OutlineSource extends Disposable { - private readonly _currentModel = observableValue('current model', undefined); + private readonly _currentModel = observableValue(this, undefined); constructor( @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts index 32d34563d46..86514a3a1ad 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts @@ -39,14 +39,14 @@ import { DiffEditorOptions } from './diffEditorOptions'; * Synchronizes scrolling. */ export class ViewZoneManager extends Disposable { - private readonly _originalTopPadding = observableValue('originalTopPadding', 0); + private readonly _originalTopPadding = observableValue(this, 0); private readonly _originalScrollTop: IObservable; - private readonly _originalScrollOffset = observableValue('originalScrollOffset', 0); + private readonly _originalScrollOffset = observableValue(this, 0); private readonly _originalScrollOffsetAnimated = animatedObservable(this._originalScrollOffset, this._store); - private readonly _modifiedTopPadding = observableValue('modifiedTopPadding', 0); + private readonly _modifiedTopPadding = observableValue(this, 0); private readonly _modifiedScrollTop: IObservable; - private readonly _modifiedScrollOffset = observableValue('modifiedScrollOffset', 0); + private readonly _modifiedScrollOffset = observableValue(this, 0); private readonly _modifiedScrollOffsetAnimated = animatedObservable(this._modifiedScrollOffset, this._store); constructor( diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 8a618685996..1cb8f2d27ca 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -29,7 +29,7 @@ export class MovedBlocksLinesPart extends Disposable { private readonly _modifiedScrollTop = observableFromEvent(this._editors.modified.onDidScrollChange, () => this._editors.modified.getScrollTop()); private readonly _viewZonesChanged = observableSignalFromEvent('onDidChangeViewZones', this._editors.modified.onDidChangeViewZones); - public readonly width = observableValue('width', 0); + public readonly width = observableValue(this, 0); constructor( private readonly _rootElement: HTMLElement, @@ -136,8 +136,8 @@ export class MovedBlocksLinesPart extends Disposable { private readonly _modifiedViewZonesChangedSignal = observableSignalFromEvent('modified.onDidChangeViewZones', this._editors.modified.onDidChangeViewZones); private readonly _originalViewZonesChangedSignal = observableSignalFromEvent('original.onDidChangeViewZones', this._editors.original.onDidChangeViewZones); - private readonly _state = derivedWithStore('state', (reader, store) => { - /** @description update moved blocks lines */ + private readonly _state = derivedWithStore((reader, store) => { + /** @description state */ this._element.replaceChildren(); const model = this._diffModel.read(reader); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts index 407fbbbb84c..352ff672edc 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts @@ -57,7 +57,7 @@ export function joinCombine(arr1: readonly T[], arr2: readonly T[], keySelect export function applyObservableDecorations(editor: ICodeEditor, decorations: IObservable): IDisposable { const d = new DisposableStore(); const decorationsCollection = editor.createDecorationsCollection(); - d.add(autorunOpts({ debugName: `Apply decorations from ${decorations.debugName}` }, reader => { + d.add(autorunOpts({ debugName: () => `Apply decorations from ${decorations.debugName}` }, reader => { const d = decorations.read(reader); decorationsCollection.set(d); })); @@ -100,8 +100,8 @@ export class ObservableElementSizeObserver extends Disposable { super(); this.elementSizeObserver = this._register(new ElementSizeObserver(element, dimension)); - this._width = observableValue('width', this.elementSizeObserver.getWidth()); - this._height = observableValue('height', this.elementSizeObserver.getHeight()); + this._width = observableValue(this, this.elementSizeObserver.getWidth()); + this._height = observableValue(this, this.elementSizeObserver.getHeight()); this._register(this.elementSizeObserver.onDidChange(e => transaction(tx => { /** @description Set width/height from elementSizeObserver */ @@ -214,8 +214,8 @@ export interface IObservableViewZone extends IViewZone { export class PlaceholderViewZone implements IObservableViewZone { public readonly domNode = document.createElement('div'); - private readonly _actualTop = observableValue('actualTop', undefined); - private readonly _actualHeight = observableValue('actualHeight', undefined); + private readonly _actualTop = observableValue(this, undefined); + private readonly _actualHeight = observableValue(this, undefined); public readonly actualTop: IObservable = this._actualTop; public readonly actualHeight: IObservable = this._actualHeight; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts index a43c994c249..0f7a28ca902 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/ghostTextWidget.ts @@ -32,7 +32,7 @@ export interface IGhostTextWidgetModel { } export class GhostTextWidget extends Disposable { - private readonly isDisposed = observableValue('isDisposed', false); + private readonly isDisposed = observableValue(this, false); private readonly currentTextModel = observableFromEvent(this.editor.onDidChangeModel, () => this.editor.getModel()); constructor( @@ -46,8 +46,7 @@ export class GhostTextWidget extends Disposable { this._register(applyObservableDecorations(this.editor, this.decorations)); } - private readonly uiState = derived(reader => { - /** @description uiState */ + private readonly uiState = derived(this, reader => { if (this.isDisposed.read(reader)) { return undefined; } @@ -126,8 +125,7 @@ export class GhostTextWidget extends Disposable { }; }); - private readonly decorations = derived(reader => { - /** @description decorations */ + private readonly decorations = derived(this, reader => { const uiState = this.uiState.read(reader); if (!uiState) { return []; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts index 094393e32c5..47e54284538 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController.ts @@ -38,8 +38,8 @@ export class InlineCompletionsController extends Disposable { } public readonly model = disposableObservableValue('inlineCompletionModel', undefined); - private readonly textModelVersionId = observableValue('textModelVersionId', -1); - private readonly cursorPosition = observableValue('cursorPosition', new Position(1, 1)); + private readonly textModelVersionId = observableValue(this, -1); + private readonly cursorPosition = observableValue(this, new Position(1, 1)); private readonly suggestWidgetAdaptor = this._register(new SuggestWidgetAdaptor( this.editor, () => this.model.get()?.selectedInlineCompletion.get()?.toSingleTextEdit(undefined), diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts index 0cde6e9a9b5..161d7cc4416 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget.ts @@ -39,8 +39,7 @@ export class InlineCompletionsHintsWidget extends Disposable { private sessionPosition: Position | undefined = undefined; - private readonly position = derived(reader => { - /** @description position */ + private readonly position = derived(this, reader => { const ghostText = this.model.read(reader)?.ghostText.read(reader); if (!this.alwaysShowToolbar.read(reader) || !ghostText || ghostText.parts.length === 0) { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index 252f9ca5f9f..b67a29ea576 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -34,11 +34,11 @@ export enum VersionIdChangeReason { export class InlineCompletionsModel extends Disposable { private readonly _source = this._register(this._instantiationService.createInstance(InlineCompletionsSource, this.textModel, this.textModelVersionId, this._debounceValue)); - private readonly _isActive = observableValue('isActive', false); + private readonly _isActive = observableValue(this, false); private readonly _forceUpdate = observableSignal('forceUpdate'); // We use a semantic id to keep the same inline completion selected even if the provider reorders the completions. - private readonly _selectedInlineCompletionId = observableValue('selectedInlineCompletionId', undefined); + private readonly _selectedInlineCompletionId = observableValue(this, undefined); private _isAcceptingPartially = false; public get isAcceptingPartially() { return this._isAcceptingPartially; } @@ -82,12 +82,14 @@ export class InlineCompletionsModel extends Disposable { VersionIdChangeReason.Undo, VersionIdChangeReason.AcceptWord, ]); - private readonly _fetchInlineCompletions = derivedHandleChanges('fetch inline completions', { + private readonly _fetchInlineCompletions = derivedHandleChanges({ + owner: this, createEmptyChangeSummary: () => ({ preserveCurrentCompletion: false, inlineCompletionTriggerKind: InlineCompletionTriggerKind.Automatic }), handleChange: (ctx, changeSummary) => { + /** @description fetch inline completions */ if (ctx.didChange(this.textModelVersionId) && this._preserveCurrentCompletionReasons.has(ctx.change)) { changeSummary.preserveCurrentCompletion = true; } else if (ctx.didChange(this._forceUpdate)) { @@ -150,8 +152,7 @@ export class InlineCompletionsModel extends Disposable { }); } - private readonly _filteredInlineCompletionItems = derived(reader => { - /** @description _filteredInlineCompletionItems */ + private readonly _filteredInlineCompletionItems = derived(this, reader => { const c = this._source.inlineCompletions.read(reader); if (!c) { return []; } const cursorPosition = this.cursorPosition.read(reader); @@ -159,8 +160,7 @@ export class InlineCompletionsModel extends Disposable { return filteredCompletions; }); - public readonly selectedInlineCompletionIndex = derived((reader) => { - /** @description selectedInlineCompletionIndex */ + public readonly selectedInlineCompletionIndex = derived(this, (reader) => { const selectedInlineCompletionId = this._selectedInlineCompletionId.read(reader); const filteredCompletions = this._filteredInlineCompletionItems.read(reader); const idx = this._selectedInlineCompletionId === undefined ? -1 @@ -173,8 +173,7 @@ export class InlineCompletionsModel extends Disposable { return idx; }); - public readonly selectedInlineCompletion = derived((reader) => { - /** @description selectedCachedCompletion */ + public readonly selectedInlineCompletion = derived(this, (reader) => { const filteredCompletions = this._filteredInlineCompletionItems.read(reader); const idx = this.selectedInlineCompletionIndex.read(reader); return filteredCompletions[idx]; @@ -184,8 +183,7 @@ export class InlineCompletionsModel extends Disposable { v => /** @description lastTriggerKind */ v?.request.context.triggerKind ); - public readonly inlineCompletionsCount = derived(reader => { - /** @description inlineCompletionsCount */ + public readonly inlineCompletionsCount = derived(this, reader => { if (this.lastTriggerKind.read(reader) === InlineCompletionTriggerKind.Explicit) { return this._filteredInlineCompletionItems.read(reader).length; } else { @@ -198,6 +196,7 @@ export class InlineCompletionsModel extends Disposable { inlineCompletion: InlineCompletionWithUpdatedRange | undefined; ghostText: GhostTextOrReplacement; } | undefined>({ + owner: this, equalityComparer: (a, b) => { if (!a || !b) { return a === b; } return ghostTextOrReplacementEquals(a.ghostText, b.ghostText) @@ -205,7 +204,6 @@ export class InlineCompletionsModel extends Disposable { && a.suggestItem === b.suggestItem; } }, (reader) => { - /** @description ghostTextAndCompletion */ const model = this.textModel; const suggestItem = this.selectedSuggestItem.read(reader); @@ -256,9 +254,9 @@ export class InlineCompletionsModel extends Disposable { } public readonly ghostText = derivedOpts({ + owner: this, equalityComparer: ghostTextOrReplacementEquals }, reader => { - /** @description ghostText */ const v = this.state.read(reader); if (!v) { return undefined; } return v.ghostText; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts index 14a7cdf1f44..99f8e5d776c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsSource.ts @@ -183,8 +183,7 @@ export class UpToDateInlineCompletions implements IDisposable { private readonly _prependedInlineCompletionItems: InlineCompletionItem[] = []; private _rangeVersionIdValue = 0; - private readonly _rangeVersionId = derived(reader => { - /** @description ranges */ + private readonly _rangeVersionId = derived(this, reader => { this.versionId.read(reader); let changed = false; for (const i of this._inlineCompletions) { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts index 7c26816b473..24c90dbdb52 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts @@ -24,7 +24,7 @@ export class SuggestWidgetAdaptor extends Disposable { private _isActive = false; private _currentSuggestItemInfo: SuggestItemInfo | undefined = undefined; - private readonly _selectedItem = observableValue('suggestWidgetInlineCompletionProvider.selectedItem', undefined as SuggestItemInfo | undefined); + private readonly _selectedItem = observableValue(this, undefined as SuggestItemInfo | undefined); public get selectedItem(): IObservable { return this._selectedItem; diff --git a/src/vs/workbench/contrib/debug/common/debugStorage.ts b/src/vs/workbench/contrib/debug/common/debugStorage.ts index 1f11c827a16..efcd601c246 100644 --- a/src/vs/workbench/contrib/debug/common/debugStorage.ts +++ b/src/vs/workbench/contrib/debug/common/debugStorage.ts @@ -22,11 +22,11 @@ const DEBUG_CHOSEN_ENVIRONMENTS_KEY = 'debug.chosenenvironment'; const DEBUG_UX_STATE_KEY = 'debug.uxstate'; export class DebugStorage extends Disposable { - public readonly breakpoints = observableValue('debugBreakpoints', this.loadBreakpoints()); - public readonly functionBreakpoints = observableValue('debugFunctionBreakpoints', this.loadFunctionBreakpoints()); - public readonly exceptionBreakpoints = observableValue('debugExceptionBreakpoints', this.loadExceptionBreakpoints()); - public readonly dataBreakpoints = observableValue('debugDataBreakpoints', this.loadDataBreakpoints()); - public readonly watchExpressions = observableValue('debugWatchExpressions', this.loadWatchExpressions()); + public readonly breakpoints = observableValue(this, this.loadBreakpoints()); + public readonly functionBreakpoints = observableValue(this, this.loadFunctionBreakpoints()); + public readonly exceptionBreakpoints = observableValue(this, this.loadExceptionBreakpoints()); + public readonly dataBreakpoints = observableValue(this, this.loadDataBreakpoints()); + public readonly watchExpressions = observableValue(this, this.loadWatchExpressions()); constructor( @IStorageService private readonly storageService: IStorageService, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts index 27d6357c6c5..cbf245b78ff 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts @@ -126,16 +126,14 @@ export class TempFileMergeEditorModeFactory implements IMergeEditorInputModelFac } class TempFileMergeEditorInputModel extends EditorModel implements IMergeEditorInputModel { - private readonly savedAltVersionId = observableValue('initialAltVersionId', this.model.resultTextModel.getAlternativeVersionId()); + private readonly savedAltVersionId = observableValue(this, this.model.resultTextModel.getAlternativeVersionId()); private readonly altVersionId = observableFromEvent( e => this.model.resultTextModel.onDidChangeContent(e), () => /** @description getAlternativeVersionId */ this.model.resultTextModel.getAlternativeVersionId() ); - public readonly isDirty = derived( - (reader) => /** @description isDirty */ this.altVersionId.read(reader) !== this.savedAltVersionId.read(reader) - ); + public readonly isDirty = derived(this, (reader) => this.altVersionId.read(reader) !== this.savedAltVersionId.read(reader)); private finished = false; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index 9ea7de40bd7..f1e508e57a7 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -32,23 +32,20 @@ export class MergeEditorModel extends EditorModel { private readonly input1TextModelDiffs = this._register(new TextModelDiffs(this.base, this.input1.textModel, this.diffComputer)); private readonly input2TextModelDiffs = this._register(new TextModelDiffs(this.base, this.input2.textModel, this.diffComputer)); private readonly resultTextModelDiffs = this._register(new TextModelDiffs(this.base, this.resultTextModel, this.diffComputer)); - public readonly modifiedBaseRanges = derived((reader) => { - /** @description modifiedBaseRanges */ + public readonly modifiedBaseRanges = derived(this, (reader) => { const input1Diffs = this.input1TextModelDiffs.diffs.read(reader); const input2Diffs = this.input2TextModelDiffs.diffs.read(reader); return ModifiedBaseRange.fromDiffs(input1Diffs, input2Diffs, this.base, this.input1.textModel, this.input2.textModel); }); - private readonly modifiedBaseRangeResultStates = - derived(reader => { - /** @description modifiedBaseRangeResultStates */ - const map = new Map( - this.modifiedBaseRanges.read(reader).map<[ModifiedBaseRange, ModifiedBaseRangeData]>((s) => [ - s, new ModifiedBaseRangeData(s) - ]) - ); - return map; - }); + private readonly modifiedBaseRangeResultStates = derived(this, reader => { + const map = new Map( + this.modifiedBaseRanges.read(reader).map<[ModifiedBaseRange, ModifiedBaseRangeData]>((s) => [ + s, new ModifiedBaseRangeData(s) + ]) + ); + return map; + }); private readonly resultSnapshot = this.resultTextModel.createSnapshot(); @@ -207,8 +204,7 @@ export class MergeEditorModel extends EditorModel { public readonly baseInput2Diffs = this.input2TextModelDiffs.diffs; public readonly baseResultDiffs = this.resultTextModelDiffs.diffs; public get isApplyingEditInResult(): boolean { return this.resultTextModelDiffs.isApplyingChange; } - public readonly input1ResultMapping = derived(reader => { - /** @description input1ResultMapping */ + public readonly input1ResultMapping = derived(this, reader => { return this.getInputResultMapping( this.baseInput1Diffs.read(reader), this.baseResultDiffs.read(reader), @@ -216,10 +212,9 @@ export class MergeEditorModel extends EditorModel { ); }); - public readonly resultInput1Mapping = derived(reader => /** @description resultInput1Mapping */ this.input1ResultMapping.read(reader).reverse()); + public readonly resultInput1Mapping = derived(this, reader => this.input1ResultMapping.read(reader).reverse()); - public readonly input2ResultMapping = derived(reader => { - /** @description input2ResultMapping */ + public readonly input2ResultMapping = derived(this, reader => { return this.getInputResultMapping( this.baseInput2Diffs.read(reader), this.baseResultDiffs.read(reader), @@ -227,7 +222,7 @@ export class MergeEditorModel extends EditorModel { ); }); - public readonly resultInput2Mapping = derived(reader => /** @description resultInput2Mapping */ this.input2ResultMapping.read(reader).reverse()); + public readonly resultInput2Mapping = derived(this, reader => this.input2ResultMapping.read(reader).reverse()); private getInputResultMapping(inputLinesDiffs: DetailedLineRangeMapping[], resultDiffs: DetailedLineRangeMapping[], inputLineCount: number) { const map = DocumentLineRangeMap.betweenOutputs(inputLinesDiffs, resultDiffs, inputLineCount); @@ -245,8 +240,7 @@ export class MergeEditorModel extends EditorModel { ); } - public readonly baseResultMapping = derived(reader => { - /** @description baseResultMapping */ + public readonly baseResultMapping = derived(this, reader => { const map = new DocumentLineRangeMap(this.baseResultDiffs.read(reader), -1); return new DocumentLineRangeMap( map.lineRangeMappings.map((m) => @@ -262,7 +256,7 @@ export class MergeEditorModel extends EditorModel { ); }); - public readonly resultBaseMapping = derived(reader => /** @description resultBaseMapping */ this.baseResultMapping.read(reader).reverse()); + public readonly resultBaseMapping = derived(this, reader => this.baseResultMapping.read(reader).reverse()); public translateInputRangeToBase(input: 1 | 2, range: Range): Range { const baseInputDiffs = input === 1 ? this.baseInput1Diffs.get() : this.baseInput2Diffs.get(); @@ -295,8 +289,7 @@ export class MergeEditorModel extends EditorModel { return this.modifiedBaseRanges.get().filter(r => r.baseRange.intersects(rangeInBase)); } - public readonly diffComputingState = derived(reader => { - /** @description diffComputingState */ + public readonly diffComputingState = derived(this, reader => { const states = [ this.input1TextModelDiffs, this.input2TextModelDiffs, @@ -312,8 +305,7 @@ export class MergeEditorModel extends EditorModel { return MergeEditorModelState.upToDate; }); - public readonly inputDiffComputingState = derived(reader => { - /** @description inputDiffComputingState */ + public readonly inputDiffComputingState = derived(this, reader => { const states = [ this.input1TextModelDiffs, this.input2TextModelDiffs, @@ -328,7 +320,7 @@ export class MergeEditorModel extends EditorModel { return MergeEditorModelState.upToDate; }); - public readonly isUpToDate = derived(reader => /** @description isUpToDate */ this.diffComputingState.read(reader) === MergeEditorModelState.upToDate); + public readonly isUpToDate = derived(this, reader => this.diffComputingState.read(reader) === MergeEditorModelState.upToDate); public readonly onInitialized = waitForState(this.diffComputingState, state => state === MergeEditorModelState.upToDate).then(() => { }); @@ -548,7 +540,7 @@ export class MergeEditorModel extends EditorModel { state.handledInput2.set(handled, tx); } - public readonly unhandledConflictsCount = derived(reader => /** @description unhandledConflictsCount */ { + public readonly unhandledConflictsCount = derived(this, reader => { const map = this.modifiedBaseRangeResultStates.read(reader); let unhandledCount = 0; for (const [_key, value] of map) { @@ -772,7 +764,7 @@ class ModifiedBaseRangeData { public computedFromDiffing = false; public previousNonDiffingState: ModifiedBaseRangeState | undefined = undefined; - public readonly handled = derived(reader => /** @description handled */ this.handledInput1.read(reader) && this.handledInput2.read(reader)); + public readonly handled = derived(this, reader => this.handledInput1.read(reader) && this.handledInput2.read(reader)); } export const enum MergeEditorModelState { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts index 2c1b5a2cca6..663973ff4af 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts @@ -17,8 +17,8 @@ import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; export class TextModelDiffs extends Disposable { private recomputeCount = 0; - private readonly _state = observableValue('LiveDiffState', TextModelDiffState.initializing); - private readonly _diffs = observableValue('LiveDiffs', []); + private readonly _state = observableValue(this, TextModelDiffState.initializing); + private readonly _diffs = observableValue(this, []); private readonly barrier = new ReentrancyBarrier(); private isDisposed = false; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts index 4e9364631b5..69b4fac15a9 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts @@ -211,8 +211,7 @@ export class ActionsSource { public readonly itemsInput1 = this.getItemsInput(1); public readonly itemsInput2 = this.getItemsInput(2); - public readonly resultItems = derived(reader => { - /** @description resultItems */ + public readonly resultItems = derived(this, reader => { const viewModel = this.viewModel; const modifiedBaseRange = this.modifiedBaseRange; @@ -321,13 +320,11 @@ export class ActionsSource { return result; }); - public readonly isEmpty = derived(reader => { - /** @description isEmpty */ + public readonly isEmpty = derived(this, reader => { return this.itemsInput1.read(reader).length + this.itemsInput2.read(reader).length + this.resultItems.read(reader).length === 0; }); - public readonly inputIsEmpty = derived(reader => { - /** @description inputIsEmpty */ + public readonly inputIsEmpty = derived(this, reader => { return this.itemsInput1.read(reader).length + this.itemsInput2.read(reader).length === 0; }); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts index aa89a47e9ce..c55a657f0d4 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts @@ -71,8 +71,7 @@ export class BaseCodeEditorView extends CodeEditorView { this._register(applyObservableDecorations(this.editor, this.decorations)); } - private readonly decorations = derived(reader => { - /** @description base.decorations */ + private readonly decorations = derived(this, reader => { const viewModel = this.viewModel.read(reader); if (!viewModel) { return []; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts index 0dbe1913d84..03cffeea7d1 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts @@ -227,8 +227,7 @@ export class ModifiedBaseRangeGutterItemModel implements IGutterItemInfo { public readonly enabled = this.model.isUpToDate; - public readonly toggleState: IObservable = derived(reader => { - /** @description checkbox is checked */ + public readonly toggleState: IObservable = derived(this, reader => { const input = this.model .getState(this.baseRange) .read(reader) @@ -238,8 +237,7 @@ export class ModifiedBaseRangeGutterItemModel implements IGutterItemInfo { : input; }); - public readonly state: IObservable<{ handled: boolean; focused: boolean }> = derived(reader => { - /** @description checkbox state */ + public readonly state: IObservable<{ handled: boolean; focused: boolean }> = derived(this, reader => { const active = this.viewModel.activeModifiedBaseRange.read(reader); if (!this.model.hasBaseRange(this.baseRange)) { return { handled: false, focused: false }; // Invalid state, should only be observed temporarily @@ -365,7 +363,7 @@ export class MergeConflictGutterItemView extends Disposable implements IGutterIt private readonly item: ISettableObservable; private readonly checkboxDiv: HTMLDivElement; - private readonly isMultiLine = observableValue('isMultiLine', false); + private readonly isMultiLine = observableValue(this, false); constructor( item: ModifiedBaseRangeGutterItemModel, @@ -374,7 +372,7 @@ export class MergeConflictGutterItemView extends Disposable implements IGutterIt ) { super(); - this.item = observableValue('item', item); + this.item = observableValue(this, item); const checkBox = new Toggle({ isChecked: false, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts index 1fdd3cf1761..54c7d9745b8 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts @@ -129,8 +129,7 @@ export class ResultCodeEditorView extends CodeEditorView { ); } - private readonly decorations = derived(reader => { - /** @description result.decorations */ + private readonly decorations = derived(this, reader => { const viewModel = this.viewModel.read(reader); if (!viewModel) { return []; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 09657513a16..6521589e940 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -58,7 +58,7 @@ export class MergeEditor extends AbstractTextEditor { static readonly ID = 'mergeEditor'; private readonly _sessionDisposables = new DisposableStore(); - private readonly _viewModel = observableValue('viewModel', undefined); + private readonly _viewModel = observableValue(this, undefined); public get viewModel(): IObservable { return this._viewModel; @@ -67,13 +67,13 @@ export class MergeEditor extends AbstractTextEditor { private rootHtmlElement: HTMLElement | undefined; private readonly _grid = this._register(new MutableDisposable>()); private readonly input1View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 1, this._viewModel)); - private readonly baseView = observableValue('baseView', undefined); - private readonly baseViewOptions = observableValue | undefined>('baseViewOptions', undefined); + private readonly baseView = observableValue(this, undefined); + private readonly baseViewOptions = observableValue | undefined>(this, undefined); private readonly input2View = this._register(this.instantiationService.createInstance(InputCodeEditorView, 2, this._viewModel)); private readonly inputResultView = this._register(this.instantiationService.createInstance(ResultCodeEditorView, this._viewModel)); private readonly _layoutMode = this.instantiationService.createInstance(MergeEditorLayoutStore); - private readonly _layoutModeObs = observableValue('layoutMode', this._layoutMode.value); + private readonly _layoutModeObs = observableValue(this, this._layoutMode.value); private readonly _ctxIsMergeEditor: IContextKey = ctxIsMergeEditor.bindTo(this.contextKeyService); private readonly _ctxUsesColumnLayout: IContextKey = ctxMergeEditorLayout.bindTo(this.contextKeyService); private readonly _ctxShowBase: IContextKey = ctxMergeEditorShowBase.bindTo(this.contextKeyService); @@ -81,7 +81,7 @@ export class MergeEditor extends AbstractTextEditor { private readonly _ctxResultUri: IContextKey = ctxMergeResultUri.bindTo(this.contextKeyService); private readonly _ctxBaseUri: IContextKey = ctxMergeBaseUri.bindTo(this.contextKeyService); private readonly _ctxShowNonConflictingChanges: IContextKey = ctxMergeEditorShowNonConflictingChanges.bindTo(this.contextKeyService); - private readonly _inputModel = observableValue('inputModel', undefined); + private readonly _inputModel = observableValue(this, undefined); public get inputModel(): IObservable { return this._inputModel; } @@ -665,7 +665,7 @@ export class MergeEditor extends AbstractTextEditor { } private readonly showNonConflictingChangesStore = this.instantiationService.createInstance(PersistentStore, 'mergeEditor/showNonConflictingChanges'); - private readonly showNonConflictingChanges = observableValue('showNonConflictingChanges', this.showNonConflictingChangesStore.get() ?? false); + private readonly showNonConflictingChanges = observableValue(this, this.showNonConflictingChangesStore.get() ?? false); public toggleShowNonConflictingChanges(): void { this.showNonConflictingChanges.set(!this.showNonConflictingChanges.get(), undefined); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts index 54934eb6ffd..36b94d8490b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts @@ -24,7 +24,7 @@ import { ResultCodeEditorView } from 'vs/workbench/contrib/mergeEditor/browser/v export class MergeEditorViewModel extends Disposable { private readonly manuallySetActiveModifiedBaseRange = observableValue< { range: ModifiedBaseRange | undefined; counter: number } - >('manuallySetActiveModifiedBaseRange', { range: undefined, counter: 0 }); + >(this, { range: undefined, counter: 0 }); private readonly attachedHistory = this._register(new AttachedHistory(this.model.resultTextModel)); @@ -95,7 +95,7 @@ export class MergeEditorViewModel extends Disposable { private counter = 0; private readonly lastFocusedEditor = derivedObservableWithWritableCache< { view: CodeEditorView | undefined; counter: number } - >('lastFocusedEditor', (reader, lastValue) => { + >(this, (reader, lastValue) => { const editors = [ this.inputCodeEditorView1, this.inputCodeEditorView2, @@ -106,8 +106,7 @@ export class MergeEditorViewModel extends Disposable { return view ? { view, counter: this.counter++ } : lastValue || { view: undefined, counter: this.counter++ }; }); - public readonly baseShowDiffAgainst = derived<1 | 2 | undefined>(reader => { - /** @description baseShowDiffAgainst */ + public readonly baseShowDiffAgainst = derived<1 | 2 | undefined>(this, reader => { const lastFocusedEditor = this.lastFocusedEditor.read(reader); if (lastFocusedEditor.view === this.inputCodeEditorView1) { return 1; @@ -117,8 +116,7 @@ export class MergeEditorViewModel extends Disposable { return undefined; }); - public readonly selectionInBase = derived(reader => { - /** @description selectionInBase */ + public readonly selectionInBase = derived(this, reader => { const sourceEditor = this.lastFocusedEditor.read(reader).view; if (!sourceEditor) { return undefined; @@ -156,7 +154,7 @@ export class MergeEditorViewModel extends Disposable { } } - public readonly activeModifiedBaseRange = derived( + public readonly activeModifiedBaseRange = derived(this, (reader) => { /** @description activeModifiedBaseRange */ const focusedEditor = this.lastFocusedEditor.read(reader); diff --git a/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts b/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts index a7c586b2442..bc98f7239ce 100644 --- a/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts +++ b/src/vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateWorkerTokenizer.ts @@ -29,7 +29,7 @@ export interface TextMateModelTokenizerHost { export class TextMateWorkerTokenizer extends MirrorTextModel { private _tokenizerWithStateStore: TokenizerWithStateStore | null = null; private _isDisposed: boolean = false; - private readonly _maxTokenizationLineLength = observableValue('_maxTokenizationLineLength', -1); + private readonly _maxTokenizationLineLength = observableValue(this, -1); private _diffStateStacksRefEqFn?: typeof diffStateStacksRefEq; private readonly _tokenizeDebouncer = new RunOnceScheduler(() => this._tokenize(), 10); From 976b2d6532acc748de17a7cfa4f9d4e853a183bd Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 4 Sep 2023 14:57:47 +0200 Subject: [PATCH 468/607] fix #192115 (#192134) --- .../contrib/logs/common/logs.contribution.ts | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index d82da53c453..59581610f9b 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -11,7 +11,7 @@ import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions' import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IFileService, whenProviderRegistered } from 'vs/platform/files/common/files'; import { IOutputChannelRegistry, IOutputService, Extensions } from 'vs/workbench/services/output/common/output'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableMap, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { CONTEXT_LOG_LEVEL, ILogService, ILoggerResource, ILoggerService, LogLevel, LogLevelToString, isLogLevel } from 'vs/platform/log/common/log'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -58,6 +58,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { private readonly contextKeys = new CounterSet(); private readonly outputChannelRegistry = Registry.as(Extensions.OutputChannels); + private readonly loggerDisposables = this._register(new DisposableMap()); constructor( @ILogService private readonly logService: ILogService, @@ -86,7 +87,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { if (visibility) { this.registerLogChannel(logger); } else { - this.outputChannelRegistry.removeChannel(logger.id); + this.deregisterLogChannel(logger); } } })); @@ -120,7 +121,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { if (this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(logger.when))) { this.registerLogChannel(logger); } else { - this.outputChannelRegistry.removeChannel(logger.id); + this.deregisterLogChannel(logger); } } } @@ -136,7 +137,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } } } - this.outputChannelRegistry.removeChannel(logger.id); + this.deregisterLogChannel(logger); } } @@ -145,27 +146,36 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { if (channel && this.uriIdentityService.extUri.isEqual(channel.file, logger.resource)) { return; } + const disposables = new DisposableStore(); const promise = createCancelablePromise(async token => { await whenProviderRegistered(logger.resource, this.fileService); try { await this.whenFileExists(logger.resource, 1, token); - const channel = this.outputChannelRegistry.getChannel(logger.id); - if (channel?.file?.scheme === Schemas.vscodeRemote) { - // Re-register the channel with new id and name - this.outputChannelRegistry.removeChannel(channel.id); - this.outputChannelRegistry.registerChannel({ id: `${channel.id}.remote`, label: nls.localize('remote name', "{0} (Remote)", channel.label), file: channel.file, log: channel.log, extensionId: channel.extensionId }); + const existingChannel = this.outputChannelRegistry.getChannel(logger.id); + const remoteLogger = existingChannel?.file?.scheme === Schemas.vscodeRemote ? this.loggerService.getRegisteredLogger(existingChannel.file) : undefined; + if (remoteLogger) { + this.deregisterLogChannel(remoteLogger); } - const hasToAppendRemote = channel && logger.resource.scheme === Schemas.vscodeRemote; + const hasToAppendRemote = existingChannel && logger.resource.scheme === Schemas.vscodeRemote; const id = hasToAppendRemote ? `${logger.id}.remote` : logger.id; const label = hasToAppendRemote ? nls.localize('remote name', "{0} (Remote)", logger.name ?? logger.id) : logger.name ?? logger.id; this.outputChannelRegistry.registerChannel({ id, label, file: logger.resource, log: true, extensionId: logger.extensionId }); + disposables.add(toDisposable(() => this.outputChannelRegistry.removeChannel(id))); + if (remoteLogger) { + this.registerLogChannel(remoteLogger); + } } catch (error) { if (!isCancellationError(error)) { this.logService.error('Error while registering log channel', logger.resource.toString(), getErrorMessage(error)); } } }); - this._register(toDisposable(() => promise.cancel())); + disposables.add(toDisposable(() => promise.cancel())); + this.loggerDisposables.set(logger.resource.toString(), disposables); + } + + private deregisterLogChannel(logger: ILoggerResource): void { + this.loggerDisposables.deleteAndDispose(logger.resource.toString()); } private async whenFileExists(file: URI, trial: number, token: CancellationToken): Promise { From d5b7644d9ba0779d1675bf5a54a895cb82533569 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 14:55:16 +0200 Subject: [PATCH 469/607] Splits up keepAlive into keepObserved and recomputeInitiallyAndOnChange --- src/vs/base/common/observable.ts | 3 +- .../base/common/observableInternal/utils.ts | 30 ++++++++++--------- src/vs/base/test/common/observable.test.ts | 4 +-- .../diffEditorWidget2/accessibleDiffViewer.ts | 4 +-- .../diffEditorWidget2/diffEditorWidget2.ts | 6 ++-- .../diffEditorWidget2/movedBlocksLines.ts | 4 +-- .../browser/inlineCompletionsModel.ts | 4 +-- .../browser/model/mergeEditorModel.ts | 8 ++--- .../textMateWorkerTokenizerController.ts | 4 +-- .../tokenizationSupportWithLineLimit.ts | 4 +-- 10 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/vs/base/common/observable.ts b/src/vs/base/common/observable.ts index 895ba31352d..2e0023bbc55 100644 --- a/src/vs/base/common/observable.ts +++ b/src/vs/base/common/observable.ts @@ -37,7 +37,8 @@ export { debouncedObservable, derivedObservableWithCache, derivedObservableWithWritableCache, - keepAlive, + keepObserved, + recomputeInitiallyAndOnChange, observableFromEvent, observableFromPromise, observableSignal, diff --git a/src/vs/base/common/observableInternal/utils.ts b/src/vs/base/common/observableInternal/utils.ts index 8717248dee9..39a31e0dda1 100644 --- a/src/vs/base/common/observableInternal/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -294,22 +294,24 @@ export function wasEventTriggeredRecently(event: Event, timeoutMs: number, return observable; } -// TODO@hediet: Have `keepCacheAlive` and `recomputeOnChange` instead of forceRecompute /** - * This ensures the observable is being observed. - * Observed observables (such as {@link derived}s) can maintain a cache, as they receive invalidation events. - * Unobserved observables are forced to recompute their value from scratch every time they are read. - * - * @param observable the observable to keep alive - * @param forceRecompute if true, the observable will be eagerly recomputed after it changed. - * Use this if recomputing the observables causes side-effects. -*/ -export function keepAlive(observable: IObservable, forceRecompute?: boolean): IDisposable { - const o = new KeepAliveObserver(forceRecompute ?? false); + * This makes sure the observable is being observed and keeps its cache alive. + */ +export function keepObserved(observable: IObservable): IDisposable { + const o = new KeepAliveObserver(false); observable.addObserver(o); - if (forceRecompute) { - observable.reportChanges(); - } + return toDisposable(() => { + observable.removeObserver(o); + }); +} + +/** + * This converts the given observable into an autorun. + */ +export function recomputeInitiallyAndOnChange(observable: IObservable): IDisposable { + const o = new KeepAliveObserver(true); + observable.addObserver(o); + observable.reportChanges(); return toDisposable(() => { observable.removeObserver(o); diff --git a/src/vs/base/test/common/observable.test.ts b/src/vs/base/test/common/observable.test.ts index 4f507c7ece7..5b1a273f767 100644 --- a/src/vs/base/test/common/observable.test.ts +++ b/src/vs/base/test/common/observable.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { Emitter, Event } from 'vs/base/common/event'; -import { ISettableObservable, autorun, derived, ITransaction, observableFromEvent, observableValue, transaction, keepAlive } from 'vs/base/common/observable'; +import { ISettableObservable, autorun, derived, ITransaction, observableFromEvent, observableValue, transaction, keepObserved } from 'vs/base/common/observable'; import { BaseObservable, IObservable, IObserver } from 'vs/base/common/observableInternal/base'; suite('observables', () => { @@ -205,7 +205,7 @@ suite('observables', () => { 'value: 5', ]); - const disposable = keepAlive(computedSum); // Use keepAlive to keep the cache + const disposable = keepObserved(computedSum); // Use keepAlive to keep the cache log.log(`value: ${computedSum.get()}`); assert.deepStrictEqual(log.getAndClearEntries(), [ 'recompute1: 1 % 3 = 1', diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts index 29022fd8bf1..3f45fae3b99 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts @@ -11,7 +11,7 @@ import { forEachAdjacent, groupAdjacentBy } from 'vs/base/common/arrays'; import { Codicon } from 'vs/base/common/codicons'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, ITransaction, autorun, autorunWithStore, derived, derivedWithStore, keepAlive, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; +import { IObservable, ITransaction, autorun, autorunWithStore, derived, derivedWithStore, recomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; @@ -51,7 +51,7 @@ export class AccessibleDiffViewer extends Disposable { @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); - this._register(keepAlive(this.model, true)); + this._register(recomputeInitiallyAndOnChange(this.model)); } private readonly model = derivedWithStore(this, (reader, store) => { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index bd99533b577..6ad55e44773 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -7,7 +7,7 @@ import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { findLast } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; -import { IObservable, autorun, autorunWithStore, derived, derivedWithStore, disposableObservableValue, keepAlive, observableValue, transaction } from 'vs/base/common/observable'; +import { IObservable, autorun, autorunWithStore, derived, derivedWithStore, disposableObservableValue, recomputeInitiallyAndOnChange, observableValue, transaction } from 'vs/base/common/observable'; import 'vs/css!./style'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions, IMouseTargetViewZone } from 'vs/editor/browser/editorBrowser'; @@ -157,7 +157,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { })); return result; }); - this._register(keepAlive(this._sash, true)); + this._register(recomputeInitiallyAndOnChange(this._sash)); this._register(autorunWithStore((reader, store) => { /** @description UnchangedRangesFeature */ @@ -218,7 +218,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { codeEditorService.addDiffEditor(this); - this._register(keepAlive(this._layoutInfo, true)); + this._register(recomputeInitiallyAndOnChange(this._layoutInfo)); this._register(autorunWithStore((reader, store) => { this.movedBlocksLinesPart.set(store.add(new (readHotReloadableExport(MovedBlocksLinesPart, reader))( diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index 1cb8f2d27ca..c8ed76eb54c 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -9,7 +9,7 @@ import { Action } from 'vs/base/common/actions'; import { booleanComparator, compareBy, findMaxIdxBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; import { Codicon } from 'vs/base/common/codicons'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, autorunHandleChanges, autorunWithStore, constObservable, derived, derivedWithStore, keepAlive, observableFromEvent, observableSignalFromEvent, observableValue } from 'vs/base/common/observable'; +import { IObservable, autorun, autorunHandleChanges, autorunWithStore, constObservable, derived, derivedWithStore, observableFromEvent, observableSignalFromEvent, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; @@ -58,7 +58,7 @@ export class MovedBlocksLinesPart extends Disposable { this._element.style.width = `${info.verticalScrollbarWidth + info.contentLeft - MovedBlocksLinesPart.movedCodeBlockPadding + this.width.read(reader)}px`; })); - this._register(keepAlive(this._state, true)); + this._register(recomputeInitiallyAndOnChange(this._state)); const movedBlockViewZones = derived(reader => { const model = this._diffModel.read(reader); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index b67a29ea576..e3e387ea66d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -6,7 +6,7 @@ import { mapFind } from 'vs/base/common/arrays'; import { BugIndicatingError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, IReader, ITransaction, autorun, derived, derivedHandleChanges, derivedOpts, keepAlive, observableSignal, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; +import { IObservable, IReader, ITransaction, autorun, derived, derivedHandleChanges, derivedOpts, recomputeInitiallyAndOnChange, observableSignal, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; import { isDefined } from 'vs/base/common/types'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; @@ -59,7 +59,7 @@ export class InlineCompletionsModel extends Disposable { ) { super(); - this._register(keepAlive(this._fetchInlineCompletions, true)); + this._register(recomputeInitiallyAndOnChange(this._fetchInlineCompletions)); let lastItem: InlineCompletionWithUpdatedRange | undefined = undefined; this._register(autorun(reader => { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index f1e508e57a7..f6a2ea598b0 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -5,7 +5,7 @@ import { CompareResult, equals } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; -import { autorunHandleChanges, derived, IObservable, IReader, ISettableObservable, ITransaction, keepAlive, observableValue, transaction, waitForState } from 'vs/base/common/observable'; +import { autorunHandleChanges, derived, IObservable, IReader, ISettableObservable, ITransaction, keepObserved, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { ILanguageService } from 'vs/editor/common/languages/language'; @@ -62,9 +62,9 @@ export class MergeEditorModel extends EditorModel { ) { super(); - this._register(keepAlive(this.modifiedBaseRangeResultStates)); - this._register(keepAlive(this.input1ResultMapping)); - this._register(keepAlive(this.input2ResultMapping)); + this._register(keepObserved(this.modifiedBaseRangeResultStates)); + this._register(keepObserved(this.input1ResultMapping)); + this._register(keepObserved(this.input2ResultMapping)); const initializePromise = this.initialize(); diff --git a/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts b/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts index f40a12b0380..850b58e1e6c 100644 --- a/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts +++ b/src/vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts @@ -5,7 +5,7 @@ import { importAMDNodeModule } from 'vs/amdX'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, autorun, keepAlive, observableFromEvent } from 'vs/base/common/observable'; +import { IObservable, autorun, keepObserved, observableFromEvent } from 'vs/base/common/observable'; import { countEOL } from 'vs/editor/common/core/eolCounter'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Range } from 'vs/editor/common/core/range'; @@ -46,7 +46,7 @@ export class TextMateWorkerTokenizerController extends Disposable { ) { super(); - this._register(keepAlive(this._loggingEnabled)); + this._register(keepObserved(this._loggingEnabled)); this._register(this._model.onDidChangeContent((e) => { if (this._shouldLog) { diff --git a/src/vs/workbench/services/textMate/browser/tokenizationSupport/tokenizationSupportWithLineLimit.ts b/src/vs/workbench/services/textMate/browser/tokenizationSupport/tokenizationSupportWithLineLimit.ts index f46faf03c24..f5bd00e965e 100644 --- a/src/vs/workbench/services/textMate/browser/tokenizationSupport/tokenizationSupportWithLineLimit.ts +++ b/src/vs/workbench/services/textMate/browser/tokenizationSupport/tokenizationSupportWithLineLimit.ts @@ -8,7 +8,7 @@ import { EncodedTokenizationResult, IBackgroundTokenizationStore, IBackgroundTok import { nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize'; import { ITextModel } from 'vs/editor/common/model'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IObservable, keepAlive } from 'vs/base/common/observable'; +import { IObservable, keepObserved } from 'vs/base/common/observable'; export class TokenizationSupportWithLineLimit extends Disposable implements ITokenizationSupport { get backgroundTokenizerShouldOnlyVerifyTokens(): boolean | undefined { @@ -22,7 +22,7 @@ export class TokenizationSupportWithLineLimit extends Disposable implements ITok ) { super(); - this._register(keepAlive(this._maxTokenizationLineLength)); + this._register(keepObserved(this._maxTokenizationLineLength)); } getInitialState(): IState { From 0f816a3513866ddd532a84b6e192efb7bd8380d8 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 15:12:16 +0200 Subject: [PATCH 470/607] Diff Algorithm refactoring --- .../{computeMoves.ts => computeMovedLines.ts} | 2 +- .../defaultLinesDiffComputer.ts | 12 ++++++------ .../heuristicSequenceOptimizations.ts | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) rename src/vs/editor/common/diff/defaultLinesDiffComputer/{computeMoves.ts => computeMovedLines.ts} (99%) diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMoves.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts similarity index 99% rename from src/vs/editor/common/diff/defaultLinesDiffComputer/computeMoves.ts rename to src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts index a148f75de3e..6436ad0d9e0 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMoves.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts @@ -14,7 +14,7 @@ import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffCo import { LineRangeFragment, isSpace } from 'vs/editor/common/diff/defaultLinesDiffComputer/utils'; import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; -export function computeMoves( +export function computeMovedLines( changes: DetailedLineRangeMapping[], originalLines: string[], modifiedLines: string[], diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts index c94f8cea008..e2de212aa40 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts @@ -12,8 +12,8 @@ import { Range } from 'vs/editor/common/core/range'; import { DateTimeout, ITimeout, InfiniteTimeout, SequenceDiff } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm'; import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing'; import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; -import { computeMoves } from 'vs/editor/common/diff/defaultLinesDiffComputer/computeMoves'; -import { extendDiffsToEntireWordIfAppropriate, optimizeSequenceDiffs, removeRandomLineMatches, removeRandomMatches, smoothenSequenceDiffs } from 'vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations'; +import { computeMovedLines } from 'vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines'; +import { extendDiffsToEntireWordIfAppropriate, optimizeSequenceDiffs, removeVeryShortMatchingLinesBetweenDiffs, removeVeryShortMatchingTextBetweenLongDiffs, removeShortMatches } from 'vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; import { DetailedLineRangeMapping, RangeMapping } from '../rangeMapping'; import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; @@ -87,7 +87,7 @@ export class DefaultLinesDiffComputer implements ILinesDiffComputer { let lineAlignments = lineAlignmentResult.diffs; let hitTimeout = lineAlignmentResult.hitTimeout; lineAlignments = optimizeSequenceDiffs(sequence1, sequence2, lineAlignments); - lineAlignments = removeRandomLineMatches(sequence1, sequence2, lineAlignments); + lineAlignments = removeVeryShortMatchingLinesBetweenDiffs(sequence1, sequence2, lineAlignments); const alignments: RangeMapping[] = []; @@ -187,7 +187,7 @@ export class DefaultLinesDiffComputer implements ILinesDiffComputer { timeout: ITimeout, considerWhitespaceChanges: boolean, ): MovedText[] { - const moves = computeMoves( + const moves = computeMovedLines( changes, originalLines, modifiedLines, @@ -217,8 +217,8 @@ export class DefaultLinesDiffComputer implements ILinesDiffComputer { let diffs = diffResult.diffs; diffs = optimizeSequenceDiffs(slice1, slice2, diffs); diffs = extendDiffsToEntireWordIfAppropriate(slice1, slice2, diffs); - diffs = smoothenSequenceDiffs(slice1, slice2, diffs); - diffs = removeRandomMatches(slice1, slice2, diffs); + diffs = removeShortMatches(slice1, slice2, diffs); + diffs = removeVeryShortMatchingTextBetweenLongDiffs(slice1, slice2, diffs); const result = diffs.map( (d) => diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts index d39fc3c93e7..b288c473874 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts @@ -196,7 +196,7 @@ function shiftDiffToBetterPosition(diff: SequenceDiff, sequence1: ISequence, seq return diff.delta(bestDelta); } -export function smoothenSequenceDiffs(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +export function removeShortMatches(sequence1: ISequence, sequence2: ISequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { const result: SequenceDiff[] = []; for (const s of sequenceDiffs) { const last = result[result.length - 1]; @@ -310,7 +310,7 @@ function mergeSequenceDiffs(sequenceDiffs1: SequenceDiff[], sequenceDiffs2: Sequ return result; } -export function removeRandomLineMatches(sequence1: LineSequence, _sequence2: LineSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +export function removeVeryShortMatchingLinesBetweenDiffs(sequence1: LineSequence, _sequence2: LineSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { let diffs = sequenceDiffs; if (diffs.length === 0) { return diffs; @@ -357,7 +357,7 @@ export function removeRandomLineMatches(sequence1: LineSequence, _sequence2: Lin return diffs; } -export function removeRandomMatches(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { +export function removeVeryShortMatchingTextBetweenLongDiffs(sequence1: LinesSliceCharSequence, sequence2: LinesSliceCharSequence, sequenceDiffs: SequenceDiff[]): SequenceDiff[] { let diffs = sequenceDiffs; if (diffs.length === 0) { return diffs; From 4a3ed95527eea750a62ad484d61c08baae571285 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 4 Sep 2023 15:40:56 +0200 Subject: [PATCH 471/607] Git - clarify git extension API usage (#192139) * Git - clarify git extension API usage * Fixed white space --- extensions/git/README.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/extensions/git/README.md b/extensions/git/README.md index 2a6678de933..97911b612ee 100644 --- a/extensions/git/README.md +++ b/extensions/git/README.md @@ -14,7 +14,13 @@ The Git extension exposes an API, reachable by any other extension. 2. Include `git.d.ts` in your extension's compilation. 3. Get a hold of the API with the following snippet: - ```ts - const gitExtension = vscode.extensions.getExtension('vscode.git').exports; - const git = gitExtension.getAPI(1); - ``` + ```ts + const gitExtension = vscode.extensions.getExtension('vscode.git').exports; + const git = gitExtension.getAPI(1); + ``` + **Note:** To ensure that the `vscode.git` extension is activated before your extension, add `extensionDependencies` ([docs](https://code.visualstudio.com/api/references/extension-manifest)) into the `package.json` of your extension: + ```json + "extensionDependencies": [ + "vscode.git" + ] + ``` From e01815dd6188efa6b8226e7128a9e663251195f1 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 4 Sep 2023 16:07:57 +0200 Subject: [PATCH 472/607] fix #188929 (#192140) --- .../test/common/globalStateSync.test.ts | 8 +++--- .../test/common/keybindingsSync.test.ts | 8 +++--- .../test/common/settingsSync.test.ts | 15 +++++++---- .../test/common/snippetsSync.test.ts | 8 +++--- .../test/common/synchronizer.test.ts | 26 +++++++++---------- .../test/common/tasksSync.test.ts | 8 +++--- .../userDataProfilesManifestSync.test.ts | 8 +++--- 7 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts index b7f6c141b02..6cf08639193 100644 --- a/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/globalStateSync.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; @@ -30,13 +30,15 @@ suite('GlobalStateSync', () => { testClient = disposableStore.add(new UserDataSyncClient(server)); await testClient.setUp(true); testObject = testClient.getSynchronizer(SyncResource.GlobalState) as GlobalStateSynchroniser; - disposableStore.add(toDisposable(() => testClient.instantiationService.get(IUserDataSyncStoreService).clear())); client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await testClient.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('when global state does not exist', () => runWithFakedTimers({ useFakeTimers: true }, async () => { assert.deepStrictEqual(await testObject.getLastSyncUserData(), null); diff --git a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts index 5cd86e8c54f..44fb14361a7 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsSync.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -25,10 +25,12 @@ suite('KeybindingsSync', () => { client = disposableStore.add(new UserDataSyncClient(server)); await client.setUp(true); testObject = client.getSynchronizer(SyncResource.Keybindings) as KeybindingsSynchroniser; - disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear())); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await client.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('when keybindings file does not exist', async () => { const fileService = client.instantiationService.get(IFileService); diff --git a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts index 6db2d5ace07..8ce9aadef2d 100644 --- a/src/vs/platform/userDataSync/test/common/settingsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/settingsSync.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; import { Event } from 'vs/base/common/event'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; @@ -42,10 +42,12 @@ suite('SettingsSync - Auto', () => { client = disposableStore.add(new UserDataSyncClient(server)); await client.setUp(true); testObject = client.getSynchronizer(SyncResource.Settings) as SettingsSynchroniser; - disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear())); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await client.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('when settings file does not exist', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const fileService = client.instantiationService.get(IFileService); @@ -536,10 +538,13 @@ suite('SettingsSync - Manual', () => { client = disposableStore.add(new UserDataSyncClient(server)); await client.setUp(true); testObject = client.getSynchronizer(SyncResource.Settings) as SettingsSynchroniser; - disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear())); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await client.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); + test('do not sync ignored settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const settingsContent = diff --git a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts index 4e50a73e94d..2b39ada8d1c 100644 --- a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; import { IStringDictionary } from 'vs/base/common/collections'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { dirname, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -159,13 +159,15 @@ suite('SnippetsSync', () => { testClient = disposableStore.add(new UserDataSyncClient(server)); await testClient.setUp(true); testObject = testClient.getSynchronizer(SyncResource.Snippets) as SnippetsSynchroniser; - disposableStore.add(toDisposable(() => testClient.instantiationService.get(IUserDataSyncStoreService).clear())); client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await testClient.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('when snippets does not exist', async () => { const fileService = testClient.instantiationService.get(IFileService); diff --git a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts index 87a6062b6af..1db10faccdc 100644 --- a/src/vs/platform/userDataSync/test/common/synchronizer.test.ts +++ b/src/vs/platform/userDataSync/test/common/synchronizer.test.ts @@ -8,7 +8,7 @@ import { Barrier } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { isEqual, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; @@ -180,16 +180,16 @@ suite('TestSynchronizer - Auto Sync', () => { const disposableStore = new DisposableStore(); const server = new UserDataSyncTestServer(); let client: UserDataSyncClient; - let userDataSyncStoreService: IUserDataSyncStoreService; setup(async () => { client = disposableStore.add(new UserDataSyncClient(server)); await client.setUp(); - userDataSyncStoreService = client.instantiationService.get(IUserDataSyncStoreService); - disposableStore.add(toDisposable(() => userDataSyncStoreService.clear())); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await client.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('status is syncing', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); @@ -487,16 +487,16 @@ suite('TestSynchronizer - Manual Sync', () => { const disposableStore = new DisposableStore(); const server = new UserDataSyncTestServer(); let client: UserDataSyncClient; - let userDataSyncStoreService: IUserDataSyncStoreService; setup(async () => { client = disposableStore.add(new UserDataSyncClient(server)); await client.setUp(); - userDataSyncStoreService = client.instantiationService.get(IUserDataSyncStoreService); - disposableStore.add(toDisposable(() => userDataSyncStoreService.clear())); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await client.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('preview', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); @@ -1065,16 +1065,16 @@ suite('TestSynchronizer - Last Sync Data', () => { const disposableStore = new DisposableStore(); const server = new UserDataSyncTestServer(); let client: UserDataSyncClient; - let userDataSyncStoreService: IUserDataSyncStoreService; setup(async () => { client = disposableStore.add(new UserDataSyncClient(server)); await client.setUp(); - userDataSyncStoreService = client.instantiationService.get(IUserDataSyncStoreService); - disposableStore.add(toDisposable(() => userDataSyncStoreService.clear())); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await client.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('last sync data is null when not synced before', () => runWithFakedTimers({ useFakeTimers: true }, async () => { const testObject: TestSynchroniser = disposableStore.add(client.instantiationService.createInstance(TestSynchroniser, { syncResource: SyncResource.Settings, profile: client.instantiationService.get(IUserDataProfilesService).defaultProfile }, undefined)); diff --git a/src/vs/platform/userDataSync/test/common/tasksSync.test.ts b/src/vs/platform/userDataSync/test/common/tasksSync.test.ts index cc9760bcf4c..01be339a733 100644 --- a/src/vs/platform/userDataSync/test/common/tasksSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/tasksSync.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; @@ -25,10 +25,12 @@ suite('TasksSync', () => { client = disposableStore.add(new UserDataSyncClient(server)); await client.setUp(true); testObject = client.getSynchronizer(SyncResource.Tasks) as TasksSynchroniser; - disposableStore.add(toDisposable(() => client.instantiationService.get(IUserDataSyncStoreService).clear())); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await client.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('when tasks file does not exist', async () => { const fileService = client.instantiationService.get(IFileService); diff --git a/src/vs/platform/userDataSync/test/common/userDataProfilesManifestSync.test.ts b/src/vs/platform/userDataSync/test/common/userDataProfilesManifestSync.test.ts index d1c7b6f572d..502dc913380 100644 --- a/src/vs/platform/userDataSync/test/common/userDataProfilesManifestSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataProfilesManifestSync.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfilesManifestSynchroniser } from 'vs/platform/userDataSync/common/userDataProfilesManifestSync'; import { ISyncData, ISyncUserDataProfile, IUserDataSyncStoreService, SyncResource, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync'; @@ -23,13 +23,15 @@ suite('UserDataProfilesManifestSync', () => { testClient = disposableStore.add(new UserDataSyncClient(server)); await testClient.setUp(true); testObject = testClient.getSynchronizer(SyncResource.Profiles) as UserDataProfilesManifestSynchroniser; - disposableStore.add(toDisposable(() => testClient.instantiationService.get(IUserDataSyncStoreService).clear())); client2 = disposableStore.add(new UserDataSyncClient(server)); await client2.setUp(true); }); - teardown(() => disposableStore.clear()); + teardown(async () => { + await testClient.instantiationService.get(IUserDataSyncStoreService).clear(); + disposableStore.clear(); + }); test('when profiles does not exist', async () => { assert.deepStrictEqual(await testObject.getLastSyncUserData(), null); From 5e0bc193c85a18756e4b45d8050b8718c724804d Mon Sep 17 00:00:00 2001 From: wickles <4229542+wickles@users.noreply.github.com> Date: Mon, 4 Sep 2023 07:20:39 -0700 Subject: [PATCH 473/607] Detect more scoop git bash paths (#192085) Fixes #192084 --- src/vs/platform/terminal/node/terminalProfiles.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index 7be7e1c3663..bf5375b17a1 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -323,6 +323,7 @@ async function getGitBashPaths(): Promise { } // Add special installs that don't follow the standard directory structure + gitBashPaths.push(`${process.env['UserProfile']}\\scoop\\apps\\git\\current\\bin\\bash.exe`); gitBashPaths.push(`${process.env['UserProfile']}\\scoop\\apps\\git-with-openssh\\current\\bin\\bash.exe`); return gitBashPaths; From 56e5b04235c16a00df1705cce7083be8e53df28a Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 4 Sep 2023 16:39:16 +0200 Subject: [PATCH 474/607] joh/marine pinniped (#192145) * merge adjacent edits when making more minimal edits * before making them more minimal, group bulk texts into buckets of simple and not so simple edits --- .../common/services/editorSimpleWorker.ts | 13 ++++++ .../services/editorSimpleWorker.test.ts | 35 +++++++++++++++ .../contrib/bulkEdit/browser/bulkTextEdits.ts | 44 ++++++++++++------- 3 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/common/services/editorSimpleWorker.ts b/src/vs/editor/common/services/editorSimpleWorker.ts index 86ae8f966a4..ef8fb85da14 100644 --- a/src/vs/editor/common/services/editorSimpleWorker.ts +++ b/src/vs/editor/common/services/editorSimpleWorker.ts @@ -511,6 +511,19 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable { return aRng - bRng; }); + // merge adjacent edits + let writeIndex = 0; + for (let readIndex = 1; readIndex < edits.length; readIndex++) { + if (Range.getEndPosition(edits[writeIndex].range).equals(Range.getStartPosition(edits[readIndex].range))) { + edits[writeIndex].range = Range.fromPositions(Range.getStartPosition(edits[writeIndex].range), Range.getEndPosition(edits[readIndex].range)); + edits[writeIndex].text += edits[readIndex].text; + } else { + writeIndex++; + edits[writeIndex] = edits[readIndex]; + } + } + edits.length = writeIndex + 1; + for (let { range, text, eol } of edits) { if (typeof eol === 'number') { diff --git a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts index f2cb9374f87..9596c1225c6 100644 --- a/src/vs/editor/test/common/services/editorSimpleWorker.test.ts +++ b/src/vs/editor/test/common/services/editorSimpleWorker.test.ts @@ -98,6 +98,41 @@ suite('EditorSimpleWorker', () => { }); }); + test('MoreMinimal, merge adjacent edits', async function () { + + const model = worker.addModel([ + 'one', + 'two', + 'three', + 'four', + 'five' + ], '\n'); + + + const newEdits = await worker.computeMoreMinimalEdits(model.uri.toString(), [ + { + range: new Range(1, 1, 2, 1), + text: 'one\ntwo\nthree\n', + }, { + range: new Range(2, 1, 3, 1), + text: '', + }, { + range: new Range(3, 1, 4, 1), + text: '', + }, { + range: new Range(4, 2, 4, 3), + text: '4', + }, { + range: new Range(5, 3, 5, 5), + text: '5', + } + ], false); + + assert.strictEqual(newEdits.length, 2); + assert.strictEqual(newEdits[0].text, '4'); + assert.strictEqual(newEdits[1].text, '5'); + }); + test('MoreMinimal, issue #15385 newline changes only', function () { const model = worker.addModel([ diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index 89f57338c7d..715b2edc5ca 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -227,7 +227,7 @@ export class BulkTextEdits { const tasks: ModelEditTask[] = []; const promises: Promise[] = []; - for (const [key, value] of this._edits) { + for (const [key, edits] of this._edits) { const promise = this._textModelResolverService.createModelReference(key).then(async ref => { let task: ModelEditTask; let makeMinimal = false; @@ -237,23 +237,37 @@ export class BulkTextEdits { } else { task = new ModelEditTask(ref); } + tasks.push(task); - for (const edit of value) { - if (makeMinimal && !edit.textEdit.insertAsSnippet) { - const newEdits = await this._editorWorker.computeMoreMinimalEdits(edit.resource, [edit.textEdit]); - if (!newEdits) { - task.addEdit(edit); - } else { - for (const moreMinialEdit of newEdits) { - task.addEdit(new ResourceTextEdit(edit.resource, moreMinialEdit, edit.versionId, edit.metadata)); - } - } - } else { - task.addEdit(edit); - } + + if (!makeMinimal) { + edits.forEach(task.addEdit, task); + return; } - tasks.push(task); + // group edits by type (snippet, metadata, or simple) and make simple groups more minimal + + const makeGroupMoreMinimal = async (start: number, end: number) => { + const oldEdits = edits.slice(start, end); + const newEdits = await this._editorWorker.computeMoreMinimalEdits(ref.object.textEditorModel.uri, oldEdits.map(e => e.textEdit), false); + if (!newEdits) { + oldEdits.forEach(task.addEdit, task); + } else { + newEdits.forEach(edit => task.addEdit(new ResourceTextEdit(ref.object.textEditorModel.uri, edit, undefined, undefined))); + } + }; + + let start = 0; + let i = 0; + for (; i < edits.length; i++) { + if (edits[i].textEdit.insertAsSnippet || edits[i].metadata) { + await makeGroupMoreMinimal(start, i); // grouped edits until now + task.addEdit(edits[i]); // this edit + start = i + 1; + } + } + await makeGroupMoreMinimal(start, i); + }); promises.push(promise); } From 014d29b8dbc5e606a79a5fae80b2be9435b1ee36 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 4 Sep 2023 16:43:26 +0200 Subject: [PATCH 475/607] no import-require please --- src/vs/base/test/common/arraysFind.test.ts | 2 +- src/vs/base/test/node/snapshot.test.ts | 2 +- src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts | 2 +- src/vs/editor/test/common/core/lineRange.test.ts | 2 +- .../notebook/test/browser/contrib/outputCopyTests.test.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/base/test/common/arraysFind.test.ts b/src/vs/base/test/common/arraysFind.test.ts index db9fcc44b82..00ef5b6112a 100644 --- a/src/vs/base/test/common/arraysFind.test.ts +++ b/src/vs/base/test/common/arraysFind.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 { MonotonousArray, findFirstMonotonous, findLastMonotonous } from 'vs/base/common/arraysFind'; suite('Arrays', () => { diff --git a/src/vs/base/test/node/snapshot.test.ts b/src/vs/base/test/node/snapshot.test.ts index 6c12ae70dd3..b7edc05d34c 100644 --- a/src/vs/base/test/node/snapshot.test.ts +++ b/src/vs/base/test/node/snapshot.test.ts @@ -8,7 +8,7 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { Promises } from 'vs/base/node/pfs'; import { SnapshotContext, assertSnapshot } from 'vs/base/test/common/snapshot'; import { URI } from 'vs/base/common/uri'; -import path = require('path'); +import * as path from 'path'; import { assertThrowsAsync } from 'vs/base/test/common/utils'; // tests for snapshot are in Node so that we can use native FS operations to diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts index 20486434dc4..63b7f923b00 100644 --- a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts +++ b/src/vs/editor/test/browser/widget/diffEditorWidget2.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 { UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; diff --git a/src/vs/editor/test/common/core/lineRange.test.ts b/src/vs/editor/test/common/core/lineRange.test.ts index 535a20607b1..919efee77a5 100644 --- a/src/vs/editor/test/common/core/lineRange.test.ts +++ b/src/vs/editor/test/common/core/lineRange.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 { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; suite('LineRange', () => { diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/outputCopyTests.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/outputCopyTests.test.ts index 3d99ff7b698..1646685196d 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/outputCopyTests.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/outputCopyTests.test.ts @@ -7,7 +7,7 @@ import { ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/noteb import { mock } from 'vs/base/test/common/mock'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ILogService } from 'vs/platform/log/common/log'; -import assert = require('assert'); +import * as assert from 'assert'; import { VSBuffer } from 'vs/base/common/buffer'; import { IOutputItemDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { copyCellOutput } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; From 9392904ae408559d1c60e120e598f5360c4e361a Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 4 Sep 2023 17:12:32 +0200 Subject: [PATCH 476/607] support diff with reading from stdin (#192149) support diff with reading from stdin. https://github.com/microsoft/vscode-remote-release/issues/8876 --- src/vs/server/node/server.cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/server/node/server.cli.ts b/src/vs/server/node/server.cli.ts index 6163df61949..e8ac628a02f 100644 --- a/src/vs/server/node/server.cli.ts +++ b/src/vs/server/node/server.cli.ts @@ -181,7 +181,7 @@ export async function main(desc: ProductDescription, args: string[]): Promise Date: Mon, 4 Sep 2023 17:29:31 +0200 Subject: [PATCH 477/607] #191860 skip until insiders is released --- test/smoke/src/areas/extensions/extensions.test.ts | 2 +- test/smoke/src/areas/workbench/localization.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index c78cbe87089..bc65a8631c4 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -7,7 +7,7 @@ import { Application, Logger } from '../../../../automation'; import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { - describe('Extensions', () => { + describe.skip('Extensions', () => { // Shared before/after handling installAllHandlers(logger); diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 12e49ce549e..865add9ae79 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -8,7 +8,7 @@ import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { - describe('Localization', () => { + describe.skip('Localization', () => { // Shared before/after handling installAllHandlers(logger); From 83c8a64a9f9d2ecede52e310c412952ba2505944 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 4 Sep 2023 17:39:14 +0200 Subject: [PATCH 478/607] fix #180695 (#192141) --- .../environment/electron-main/environmentMainService.ts | 4 ---- src/vs/platform/userData/common/fileUserDataProvider.ts | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index aab03b3130f..748ff075783 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -6,7 +6,6 @@ import { memoize } from 'vs/base/common/decorators'; import { join } from 'vs/base/common/path'; import { isLinux } from 'vs/base/common/platform'; -import { URI } from 'vs/base/common/uri'; import { createStaticIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; @@ -69,9 +68,6 @@ export class EnvironmentMainService extends NativeEnvironmentService implements @memoize get useCodeCache(): boolean { return !!this.codeCachePath; } - @memoize - override get userRoamingDataHome(): URI { return this.appSettingsHome; } - unsetSnapExportedVariables() { if (!isLinux) { return; diff --git a/src/vs/platform/userData/common/fileUserDataProvider.ts b/src/vs/platform/userData/common/fileUserDataProvider.ts index 539c8011f3e..9382a676acb 100644 --- a/src/vs/platform/userData/common/fileUserDataProvider.ts +++ b/src/vs/platform/userData/common/fileUserDataProvider.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ 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, IFileSystemProviderWithFileFolderCopyCapability, hasFileFolderCopyCapability } from 'vs/platform/files/common/files'; +import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability, IFileSystemProviderWithFileFolderCopyCapability, hasFileFolderCopyCapability, hasFileAtomicWriteCapability } 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'; import { ILogService } from 'vs/platform/log/common/log'; import { TernarySearchTree } from 'vs/base/common/ternarySearchTree'; import { VSBuffer } from 'vs/base/common/buffer'; +import { isObject } from 'vs/base/common/types'; /** * This is a wrapper on top of the local filesystem provider which will @@ -85,6 +86,9 @@ export class FileUserDataProvider extends Disposable implements } writeFile(resource: URI, content: Uint8Array, opts: IFileWriteOptions): Promise { + if (!isObject(opts.atomic) && hasFileAtomicWriteCapability(this.fileSystemProvider)) { + opts = { ...opts, atomic: { postfix: '.vsctmp' } }; + } return this.fileSystemProvider.writeFile(this.toFileSystemResource(resource), content, opts); } From 65e921c5b8e09c11fa75141a3aa33ca8cae6dbef Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 17:36:29 +0200 Subject: [PATCH 479/607] Removes support to pass in diff algorithm via option in favor of a service. Fixes https://github.com/microsoft/monaco-editor/issues/3558 --- build/monaco/monaco.d.ts.recipe | 4 - .../diffEditorWidget2/diffEditorViewModel.ts | 26 ++- .../diffEditorWidget2/diffEditorWidget2.ts | 16 +- .../diffProviderFactoryService.ts | 35 ++++ src/vs/editor/common/config/editorOptions.ts | 3 +- .../common/diff/documentDiffProvider.ts | 3 + .../standalone/browser/standaloneEditor.ts | 38 ++-- src/vs/monaco.d.ts | 180 +----------------- 8 files changed, 80 insertions(+), 225 deletions(-) create mode 100644 src/vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService.ts diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 4f064aeb6f1..759ba84e090 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -108,10 +108,6 @@ export interface ICommandHandler { #include(vs/editor/common/core/wordHelper): IWordAtPosition #includeAll(vs/editor/common/model): IScrollEvent #include(vs/editor/common/diff/legacyLinesDiffComputer): IChange, ICharChange, ILineChange -#include(vs/editor/common/diff/documentDiffProvider): IDocumentDiffProvider, IDocumentDiffProviderOptions, IDocumentDiff -#include(vs/editor/common/core/lineRange): LineRange -#include(vs/editor/common/diff/linesDiffComputer): MovedText -#include(vs/editor/common/diff/rangeMapping): DetailedLineRangeMapping, RangeMapping, LineRangeMapping #include(vs/editor/common/core/dimension): IDimension #includeAll(vs/editor/common/editorCommon): IScrollEvent #includeAll(vs/editor/common/textModelEvents): diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts index 60bd6f8cba8..da024fec498 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts @@ -7,10 +7,12 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; +import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService'; import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; -import { IDocumentDiff, IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; +import { IDocumentDiff } from 'vs/editor/common/diff/documentDiffProvider'; import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IDiffEditorModel, IDiffEditorViewModel } from 'vs/editor/common/editorCommon'; @@ -64,10 +66,22 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo private readonly _cancellationTokenSource = new CancellationTokenSource(); + private readonly _diffProvider = derived(this, reader => { + const diffProvider = this._diffProviderFactoryService.createDiffProvider(this._editor, { + diffAlgorithm: this._options.diffAlgorithm.read(reader) + }); + const onChangeSignal = observableSignalFromEvent('onDidChange', diffProvider.onDidChange); + return { + diffProvider, + onChangeSignal, + }; + }); + constructor( public readonly model: IDiffEditorModel, private readonly _options: DiffEditorOptions, - documentDiffProvider: IDocumentDiffProvider, + private readonly _editor: IDiffEditor, + @IDiffProviderFactoryService private readonly _diffProviderFactoryService: IDiffProviderFactoryService, ) { super(); @@ -162,8 +176,6 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo debouncer.schedule(); })); - const documentDiffProviderOptionChanged = observableSignalFromEvent('documentDiffProviderOptionChanged', documentDiffProvider.onDidChange); - this._register(autorunWithStore(async (reader, store) => { /** @description compute diff */ @@ -173,7 +185,9 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo debouncer.cancel(); contentChangedSignal.read(reader); - documentDiffProviderOptionChanged.read(reader); + const documentDiffProvider = this._diffProvider.read(reader); + documentDiffProvider.onChangeSignal.read(reader); + readHotReloadableExport(DefaultLinesDiffComputer, reader); this._isDiffUpToDate.set(false, undefined); @@ -190,7 +204,7 @@ export class DiffEditorViewModel extends Disposable implements IDiffEditorViewMo modifiedTextEditInfos = combineTextEditInfos(modifiedTextEditInfos, edits); })); - let result = await documentDiffProvider.computeDiff(model.original, model.modified, { + let result = await documentDiffProvider.diffProvider.computeDiff(model.original, model.modified, { ignoreTrimWhitespace: this._options.ignoreTrimWhitespace.read(reader), maxComputationTimeMs: this._options.maxComputationTimeMs.read(reader), computeMoves: this._options.showMoves.read(reader), diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 6ad55e44773..e899a379f99 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -7,6 +7,7 @@ import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { findLast } from 'vs/base/common/arrays'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; +import { toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, autorunWithStore, derived, derivedWithStore, disposableObservableValue, recomputeInitiallyAndOnChange, observableValue, transaction } from 'vs/base/common/observable'; import 'vs/css!./style'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; @@ -18,19 +19,18 @@ import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEdito import { AccessibleDiffViewer } from 'vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer'; import { DiffEditorDecorations } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations'; import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorSash'; +import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature'; import { ViewZoneManager } from 'vs/editor/browser/widget/diffEditorWidget2/lineAlignment'; import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines'; import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart'; -import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature'; import { CSSStyle, ObservableElementSizeObserver, applyStyle, readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; -import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IDimension } from 'vs/editor/common/core/dimension'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { CursorChangeReason } from 'vs/editor/common/cursorEvents'; -import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { EditorType, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; @@ -39,13 +39,12 @@ import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioC import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IEditorProgressService } from 'vs/platform/progress/common/progress'; import './colors'; import { DelegatingEditor } from './delegatingEditorImpl'; import { DiffEditorEditors } from './diffEditorEditors'; import { DiffEditorOptions } from './diffEditorOptions'; import { DiffEditorViewModel, DiffMapping, DiffState } from './diffEditorViewModel'; -import { toDisposable } from 'vs/base/common/lifecycle'; -import { IEditorProgressService } from 'vs/platform/progress/common/progress'; export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { private readonly elements = h('div.monaco-diff-editor.side-by-side', { style: { position: 'relative', height: '100%' } }, [ @@ -366,12 +365,7 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { } public createViewModel(model: IDiffEditorModel): IDiffEditorViewModel { - return new DiffEditorViewModel( - model, - this._options, - // TODO@hediet make diffAlgorithm observable - this._instantiationService.createInstance(WorkerBasedDocumentDiffProvider, { diffAlgorithm: this._options.diffAlgorithm.get() }) - ); + return this._instantiationService.createInstance(DiffEditorViewModel, model, this._options, this); } override getModel(): IDiffEditorModel | null { return this._diffModel.get()?.model ?? null; } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService.ts new file mode 100644 index 00000000000..fd7bb19ac4b --- /dev/null +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider'; +import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; +import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IDiffProviderFactoryService = createDecorator('diffProviderFactoryService'); + +export interface IDocumentDiffProviderOptions { + readonly diffAlgorithm?: 'legacy' | 'advanced'; +} + +export interface IDiffProviderFactoryService { + readonly _serviceBrand: undefined; + createDiffProvider(editor: IDiffEditor, options: IDocumentDiffProviderOptions): IDocumentDiffProvider; +} + +export class DiffProviderFactoryService implements IDiffProviderFactoryService { + readonly _serviceBrand: undefined; + + constructor( + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { } + + createDiffProvider(editor: IDiffEditor, options: IDocumentDiffProviderOptions): IDocumentDiffProvider { + return this.instantiationService.createInstance(WorkerBasedDocumentDiffProvider, options); + } +} + +registerSingleton(IDiffProviderFactoryService, DiffProviderFactoryService, InstantiationType.Delayed); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 35979acc29f..1604747fdb9 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -13,7 +13,6 @@ import { Constants } from 'vs/base/common/uint'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/core/textModelDefaults'; import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/core/wordHelper'; -import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; import * as nls from 'vs/nls'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; @@ -817,7 +816,7 @@ export interface IDiffEditorBaseOptions { /** * Diff Algorithm */ - diffAlgorithm?: 'legacy' | 'advanced' | IDocumentDiffProvider; + diffAlgorithm?: 'legacy' | 'advanced'; /** * Whether the diff editor aria label should be verbose. diff --git a/src/vs/editor/common/diff/documentDiffProvider.ts b/src/vs/editor/common/diff/documentDiffProvider.ts index 44accf9e604..10dfca90888 100644 --- a/src/vs/editor/common/diff/documentDiffProvider.ts +++ b/src/vs/editor/common/diff/documentDiffProvider.ts @@ -11,6 +11,7 @@ import { ITextModel } from 'vs/editor/common/model'; /** * A document diff provider computes the diff between two text models. + * @internal */ export interface IDocumentDiffProvider { /** @@ -27,6 +28,7 @@ export interface IDocumentDiffProvider { /** * Options for the diff computation. + * @internal */ export interface IDocumentDiffProviderOptions { /** @@ -47,6 +49,7 @@ export interface IDocumentDiffProviderOptions { /** * Represents a diff between two text models. + * @internal */ export interface IDocumentDiff { /** diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 9d529b71049..8f8caf549a3 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -3,45 +3,42 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./standalone-tokens'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { splitLines } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; +import 'vs/css!./standalone-tokens'; import { FontMeasurements } from 'vs/editor/browser/config/fontMeasurements'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from 'vs/editor/browser/services/webWorker'; import { DiffNavigator, IDiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { ApplyUpdateResult, ConfigurationChangedEvent, EditorOptions } from 'vs/editor/common/config/editorOptions'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; +import { IPosition } from 'vs/editor/common/core/position'; +import { IRange } from 'vs/editor/common/core/range'; import { EditorType, IDiffEditor } from 'vs/editor/common/editorCommon'; -import { FindMatch, ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import * as languages from 'vs/editor/common/languages'; -import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; -import { NullState, nullTokenize } from 'vs/editor/common/languages/nullTokenize'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; +import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { NullState, nullTokenize } from 'vs/editor/common/languages/nullTokenize'; +import { FindMatch, ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; -import { createWebWorker as actualCreateWebWorker, IWebWorkerOptions, MonacoWebWorker } from 'vs/editor/browser/services/webWorker'; import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums'; import { Colorizer, IColorizerElementOptions, IColorizerOptions } from 'vs/editor/standalone/browser/colorizer'; -import { createTextModel, IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor, StandaloneDiffEditor2, StandaloneEditor } from 'vs/editor/standalone/browser/standaloneCodeEditor'; +import { IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor, StandaloneDiffEditor2, StandaloneEditor, createTextModel } from 'vs/editor/standalone/browser/standaloneCodeEditor'; import { IEditorOverrideServices, StandaloneKeybindingService, StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices'; import { StandaloneThemeService } from 'vs/editor/standalone/browser/standaloneThemeService'; import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; +import { IMenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; -import { IMarker, IMarkerData, IMarkerService } from 'vs/platform/markers/common/markers'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { IMenuItem, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; -import { MovedText } from 'vs/editor/common/diff/linesDiffComputer'; -import { DetailedLineRangeMapping, RangeMapping, LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; -import { LineRange } from 'vs/editor/common/core/lineRange'; -import { EditorZoom } from 'vs/editor/common/config/editorZoom'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IRange } from 'vs/editor/common/core/range'; -import { IPosition } from 'vs/editor/common/core/position'; import { ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IMarker, IMarkerData, IMarkerService } from 'vs/platform/markers/common/markers'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; /** * Create a new editor under `domElement`. @@ -584,12 +581,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor { TextModelResolvedOptions: TextModelResolvedOptions, FindMatch: FindMatch, ApplyUpdateResult: ApplyUpdateResult, - LineRange: LineRange, - DetailedLineRangeMapping: DetailedLineRangeMapping, - RangeMapping: RangeMapping, EditorZoom: EditorZoom, - MovedText: MovedText, - LineRangeMapping: LineRangeMapping, // vars EditorType: EditorType, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index b6519772cc2..168b46965be 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2366,184 +2366,6 @@ declare namespace monaco.editor { export interface ILineChange extends IChange { readonly charChanges: ICharChange[] | undefined; } - - /** - * A document diff provider computes the diff between two text models. - */ - export interface IDocumentDiffProvider { - /** - * Computes the diff between the text models `original` and `modified`. - */ - computeDiff(original: ITextModel, modified: ITextModel, options: IDocumentDiffProviderOptions, cancellationToken: CancellationToken): Promise; - /** - * Is fired when settings of the diff algorithm change that could alter the result of the diffing computation. - * Any user of this provider should recompute the diff when this event is fired. - */ - onDidChange: IEvent; - } - - /** - * Options for the diff computation. - */ - export interface IDocumentDiffProviderOptions { - /** - * When set to true, the diff should ignore whitespace changes. - */ - ignoreTrimWhitespace: boolean; - /** - * A diff computation should throw if it takes longer than this value. - */ - maxComputationTimeMs: number; - /** - * If set, the diff computation should compute moves in addition to insertions and deletions. - */ - computeMoves: boolean; - } - - /** - * Represents a diff between two text models. - */ - export interface IDocumentDiff { - /** - * If true, both text models are identical (byte-wise). - */ - readonly identical: boolean; - /** - * If true, the diff computation timed out and the diff might not be accurate. - */ - readonly quitEarly: boolean; - /** - * Maps all modified line ranges in the original to the corresponding line ranges in the modified text model. - */ - readonly changes: readonly DetailedLineRangeMapping[]; - /** - * Sorted by original line ranges. - * The original line ranges and the modified line ranges must be disjoint (but can be touching). - */ - readonly moves: readonly MovedText[]; - } - - /** - * A range of lines (1-based). - */ - export class LineRange { - static fromRange(range: Range): LineRange; - static subtract(a: LineRange, b: LineRange | undefined): LineRange[]; - /** - * @param lineRanges An array of sorted line ranges. - */ - static joinMany(lineRanges: readonly (readonly LineRange[])[]): readonly LineRange[]; - static ofLength(startLineNumber: number, length: number): LineRange; - /** - * The start line number. - */ - readonly startLineNumber: number; - /** - * The end line number (exclusive). - */ - readonly endLineNumberExclusive: number; - constructor(startLineNumber: number, endLineNumberExclusive: number); - /** - * Indicates if this line range contains the given line number. - */ - contains(lineNumber: number): boolean; - /** - * Indicates if this line range is empty. - */ - get isEmpty(): boolean; - /** - * Moves this line range by the given offset of line numbers. - */ - delta(offset: number): LineRange; - deltaLength(offset: number): LineRange; - /** - * The number of lines this line range spans. - */ - get length(): number; - /** - * Creates a line range that combines this and the given line range. - */ - join(other: LineRange): LineRange; - toString(): string; - /** - * The resulting range is empty if the ranges do not intersect, but touch. - * If the ranges don't even touch, the result is undefined. - */ - intersect(other: LineRange): LineRange | undefined; - intersectsStrict(other: LineRange): boolean; - overlapOrTouch(other: LineRange): boolean; - equals(b: LineRange): boolean; - toInclusiveRange(): Range | null; - toExclusiveRange(): Range; - mapToLineArray(f: (lineNumber: number) => T): T[]; - forEach(f: (lineNumber: number) => void): void; - includes(lineNumber: number): boolean; - } - - export class MovedText { - readonly lineRangeMapping: LineRangeMapping; - /** - * The diff from the original text to the moved text. - * Must be contained in the original/modified line range. - * Can be empty if the text didn't change (only moved). - */ - readonly changes: readonly DetailedLineRangeMapping[]; - constructor(lineRangeMapping: LineRangeMapping, changes: readonly DetailedLineRangeMapping[]); - flip(): MovedText; - } - - /** - * Maps a line range in the original text model to a line range in the modified text model. - * Also contains inner range mappings. - */ - export class DetailedLineRangeMapping extends LineRangeMapping { - /** - * If inner changes have not been computed, this is set to undefined. - * Otherwise, it represents the character-level diff in this line range. - * The original range of each range mapping should be contained in the original line range (same for modified), exceptions are new-lines. - * Must not be an empty array. - */ - readonly innerChanges: RangeMapping[] | undefined; - constructor(originalRange: LineRange, modifiedRange: LineRange, innerChanges: RangeMapping[] | undefined); - flip(): DetailedLineRangeMapping; - } - - /** - * Maps a range in the original text model to a range in the modified text model. - */ - export class RangeMapping { - /** - * The original range. - */ - readonly originalRange: Range; - /** - * The modified range. - */ - readonly modifiedRange: Range; - constructor(originalRange: Range, modifiedRange: Range); - toString(): string; - flip(): RangeMapping; - } - - /** - * Maps a line range in the original text model to a line range in the modified text model. - */ - export class LineRangeMapping { - static inverse(mapping: readonly DetailedLineRangeMapping[], originalLineCount: number, modifiedLineCount: number): DetailedLineRangeMapping[]; - /** - * The line range in the original text model. - */ - readonly original: LineRange; - /** - * The line range in the modified text model. - */ - readonly modified: LineRange; - constructor(originalRange: LineRange, modifiedRange: LineRange); - toString(): string; - flip(): LineRangeMapping; - join(other: LineRangeMapping): LineRangeMapping; - get changedLineCount(): any; - } export interface IDimension { width: number; height: number; @@ -3966,7 +3788,7 @@ declare namespace monaco.editor { /** * Diff Algorithm */ - diffAlgorithm?: 'legacy' | 'advanced' | IDocumentDiffProvider; + diffAlgorithm?: 'legacy' | 'advanced'; /** * Whether the diff editor aria label should be verbose. */ From 09a9c0140e8c6872d1b5498289286fce7caac688 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Mon, 4 Sep 2023 18:00:07 +0200 Subject: [PATCH 480/607] Support different tab heights --- .../lib/stylelint/vscode-known-variables.json | 1 + .../workbench/browser/parts/editor/editor.ts | 1 + .../parts/editor/media/tabstitlecontrol.css | 8 +++---- .../parts/editor/media/titlecontrol.css | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 21 +++++++++++++++---- .../browser/workbench.contribution.ts | 6 ++++++ src/vs/workbench/common/editor.ts | 1 + 7 files changed, 31 insertions(+), 9 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 1fb81747019..62b9f2fc563 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -734,6 +734,7 @@ "--tab-sizing-current-width", "--tab-sizing-fixed-min-width", "--tab-sizing-fixed-max-width", + "--tab-height", "--testMessageDecorationFontFamily", "--testMessageDecorationFontSize", "--title-border-bottom-color", diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 23f3371411a..c8cdf695fcb 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -31,6 +31,7 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = { tabSizingFixedMinWidth: 50, tabSizingFixedMaxWidth: 160, pinnedTabSizing: 'normal', + tabHeight: 'normal', preventPinnedEditorClose: 'keyboardAndMouse', titleScrollbarSizing: 'default', focusRecentEditorAfterClose: true, diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 17e17c1a758..62de2b2dc11 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -71,7 +71,7 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container { display: flex; - height: 35px; + height: var(--tab-height); scrollbar-width: none; /* Firefox: hide scrollbar */ } @@ -97,7 +97,7 @@ display: flex; white-space: nowrap; cursor: pointer; - height: 35px; + height: var(--tab-height); box-sizing: border-box; padding-left: 10px; } @@ -265,7 +265,7 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { margin-top: auto; margin-bottom: auto; - line-height: 35px; /* aligns icon and label vertically centered in the tab */ + line-height: var(--tab-height); /* aligns icon and label vertically centered in the tab */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label, @@ -473,7 +473,7 @@ cursor: default; flex: initial; padding: 0 8px 0 4px; - height: 35px; + height: var(--tab-height); } .monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-item { diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 378d2bfa63f..d627fc05a5b 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -30,7 +30,7 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before { - height: 35px; /* tweak the icon size of the editor labels when icons are enabled */ + height: var(--tab-height); /* tweak the icon size of the editor labels when icons are enabled */ } .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .monaco-icon-label::after, diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 5d5d132b05d..153ba1472b6 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -97,8 +97,6 @@ export class TabsTitleControl extends TitleControl { fit: 120 }; - private static readonly TAB_HEIGHT = 35; - private static readonly DRAG_OVER_OPEN_TAB_THRESHOLD = 1500; private static readonly MOUSE_WHEEL_EVENT_THRESHOLD = 150; @@ -182,6 +180,7 @@ export class TabsTitleControl extends TitleControl { this.tabSizingFixedDisposables = this._register(new DisposableStore()); this.updateTabSizing(false); + this.updateTabHeight(); // Tabs Scrollbar this.tabsScrollbar = this.createTabsScrollbar(this.tabsContainer); @@ -269,6 +268,11 @@ export class TabsTitleControl extends TitleControl { }); } + private updateTabHeight(): void { + const tabsContainer = assertIsDefined(this.tabsContainer); + tabsContainer.style.setProperty('--tab-height', `${this.getTabHeight()}px`); + } + private getTabsScrollbarSizing(): number { if (this.accessor.partOptions.titleScrollbarSizing !== 'large') { return TabsTitleControl.SCROLLBAR_SIZES.default; @@ -731,6 +735,11 @@ export class TabsTitleControl extends TitleControl { this.updateTabSizing(true); } + // Update tab height + if (oldOptions.tabHeight !== newOptions.tabHeight) { + this.updateTabHeight(); + } + // Redraw tabs when other options change if ( oldOptions.labelFormat !== newOptions.labelFormat || @@ -1549,7 +1558,7 @@ export class TabsTitleControl extends TitleControl { if (this.accessor.partOptions.wrapTabs && this.tabsAndActionsContainer?.classList.contains('wrapping')) { total = this.tabsAndActionsContainer.offsetHeight; } else { - total = TabsTitleControl.TAB_HEIGHT; + total = this.getTabHeight(); } const offset = total; @@ -1562,6 +1571,10 @@ export class TabsTitleControl extends TitleControl { return { total, offset }; } + getTabHeight() { + return this.accessor.partOptions.tabHeight !== 'small' ? 35 : 22; + } + layout(dimensions: ITitleControlDimensions, options?: ITabsTitleControlLayoutOptions): Dimension { // Remember dimensions that we get @@ -1707,7 +1720,7 @@ export class TabsTitleControl extends TitleControl { if (tabsWrapMultiLine) { if ( (tabsContainer.offsetHeight > dimensions.available.height) || // if height exceeds available height - (allTabsWidth === visibleTabsWidth && tabsContainer.offsetHeight === TabsTitleControl.TAB_HEIGHT) || // if wrapping is not needed anymore + (allTabsWidth === visibleTabsWidth && tabsContainer.offsetHeight === this.getTabHeight()) || // if wrapping is not needed anymore (!lastTabFitsWrapped()) // if last tab does not fit anymore ) { updateTabsWrapping(false); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index a0aa8d51b7b..85d699dfbf5 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -165,6 +165,12 @@ const registry = Registry.as(ConfigurationExtensions.Con 'minimum': 38, 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.editor.tabSizingFixedMaxWidth' }, "Controls the maximum width of tabs when `#workbench.editor.tabSizing#` size is set to `fixed`.") }, + 'workbench.editor.tabHeight': { + 'type': 'string', + 'enum': ['normal', 'small'], + 'default': 'normal', + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.editor.tabHeight' }, "Controls the height of editor tabs.") + }, 'workbench.editor.pinnedTabSizing': { 'type': 'string', 'enum': ['normal', 'compact', 'shrink'], diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 356f88a5962..9414e6004df 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1098,6 +1098,7 @@ interface IEditorPartConfiguration { tabSizingFixedMinWidth?: number; tabSizingFixedMaxWidth?: number; pinnedTabSizing?: 'normal' | 'compact' | 'shrink'; + tabHeight?: 'normal' | 'small'; preventPinnedEditorClose?: PreventPinnedEditorClose; titleScrollbarSizing?: 'default' | 'large'; focusRecentEditorAfterClose?: boolean; From 3159fa24938a12834509471179a48bb3df1da2f6 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 4 Sep 2023 18:26:25 +0200 Subject: [PATCH 481/607] update playwright --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 4a0405868b6..2814401b7f9 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,7 @@ "yazl": "^2.4.3" }, "devDependencies": { - "@playwright/test": "^1.34.3", + "@playwright/test": "^1.37.1", "@swc/cli": "0.1.62", "@swc/core": "1.3.62", "@types/cookie": "^0.3.3", diff --git a/yarn.lock b/yarn.lock index e2a9ec02d7b..c49176e5cb6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -682,13 +682,13 @@ node-addon-api "^3.2.1" node-gyp-build "^4.3.0" -"@playwright/test@^1.34.3": - version "1.34.3" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.34.3.tgz#d9f1ac3f1a09633b5ca5351c50c308bf802bde53" - integrity sha512-zPLef6w9P6T/iT6XDYG3mvGOqOyb6eHaV9XtkunYs0+OzxBtrPAAaHotc0X+PJ00WPPnLfFBTl7mf45Mn8DBmw== +"@playwright/test@^1.37.1": + version "1.37.1" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.37.1.tgz#e7f44ae0faf1be52d6360c6bbf689fd0057d9b6f" + integrity sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg== dependencies: "@types/node" "*" - playwright-core "1.34.3" + playwright-core "1.37.1" optionalDependencies: fsevents "2.3.2" @@ -7827,10 +7827,10 @@ playwright-core@1.32.2: resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.32.2.tgz#608810c3c4486fb86a224732ac0d3560a96ded8b" integrity sha512-zD7aonO+07kOTthsrCR3YCVnDcqSHIJpdFUtZEMOb6//1Rc7/6mZDRdw+nlzcQiQltOOsiqI3rrSyn/SlyjnJQ== -playwright-core@1.34.3: - version "1.34.3" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.34.3.tgz#bc906ea1b26bb66116ce329436ee59ba2e78fe9f" - integrity sha512-2pWd6G7OHKemc5x1r1rp8aQcpvDh7goMBZlJv6Co5vCNLVcQJdhxRL09SGaY6HcyHH9aT4tiynZabMofVasBYw== +playwright-core@1.37.1: + version "1.37.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.37.1.tgz#cb517d52e2e8cb4fa71957639f1cd105d1683126" + integrity sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA== playwright@^1.29.2: version "1.30.0" From a10be9fb4cf30bb1f69033e241ee65c689a9746c Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 4 Sep 2023 18:26:57 +0200 Subject: [PATCH 482/607] skip `smart diff consistency` because it fails on webkit and causes other tests to fail... --- src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index 9ae0e08b0f1..6b60b35aef6 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -380,7 +380,7 @@ suite('IndexTreeModel', () => { assert.deepStrictEqual(list[5].depth, 1); })); - test('smart diff consistency', () => { + test.skip('smart diff consistency', () => { const times = 500; const minEdits = 1; const maxEdits = 10; From b9e4141833a7b88776c4eba83f1e835a7f58604f Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 4 Sep 2023 20:43:33 +0200 Subject: [PATCH 483/607] Revert "Remove superfluous arg in git smoke.test.ts (#173194)" (#192161) This reverts commit 9dd556a9e06a6f9b5d7e734fea9ec00d34071a63. --- extensions/git/src/test/smoke.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/test/smoke.test.ts b/extensions/git/src/test/smoke.test.ts index b0070eb127f..789086e90a4 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -122,7 +122,7 @@ suite('git smoke test', function () { repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED); assert.strictEqual(repository.state.indexChanges.length, 0); - await commands.executeCommand('git.stageAll'); + await commands.executeCommand('git.stageAll', appjs); await repository.commit('third commit'); assert.strictEqual(repository.state.workingTreeChanges.length, 0); assert.strictEqual(repository.state.indexChanges.length, 0); From e0e970f76bc3746d159cefaef3b02b7ce7e18cb7 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 4 Sep 2023 22:45:27 +0200 Subject: [PATCH 484/607] Removes legacy diff editor. (#191989) * Removes legacy diff editor. * Fixes CI * Fixes CI --- .vscode/settings.json | 1 - build/monaco/monaco.d.ts.recipe | 1 - .../editor/browser/widget/diffEditorWidget.ts | 2802 ----------------- .../diffEditorWidget2/accessibleDiffViewer.ts | 12 +- .../diffEditorWidget2/diffEditorEditors.ts | 2 +- .../diffEditorWidget2/diffEditorWidget2.ts | 12 +- .../widget/diffEditorWidget2/renderLines.ts | 4 +- src/vs/editor/browser/widget/diffNavigator.ts | 278 -- src/vs/editor/browser/widget/diffReview.ts | 826 ----- .../widget/embeddedCodeEditorWidget.ts | 63 +- .../config/editorConfigurationSchema.ts | 16 +- src/vs/editor/editor.all.ts | 3 +- .../browser/standaloneCodeEditor.ts | 77 - .../standalone/browser/standaloneEditor.ts | 20 +- src/vs/monaco.d.ts | 15 - .../browser/parts/editor/textDiffEditor.ts | 22 +- .../codeEditor/browser/diffEditorHelper.ts | 4 +- .../notebook/browser/diff/diffComponents.ts | 16 +- .../browser/diff/diffElementViewModel.ts | 6 +- .../browser/diff/notebookDiffEditorBrowser.ts | 4 +- .../notebook/browser/diff/notebookDiffList.ts | 4 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 8 +- 22 files changed, 57 insertions(+), 4139 deletions(-) delete mode 100644 src/vs/editor/browser/widget/diffEditorWidget.ts delete mode 100644 src/vs/editor/browser/widget/diffNavigator.ts delete mode 100644 src/vs/editor/browser/widget/diffReview.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 7eefe0c57f6..6925e3ed8c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -151,5 +151,4 @@ "application.experimental.rendererProfiling": true, "editor.experimental.asyncTokenization": true, "editor.experimental.asyncTokenizationVerification": true, - "diffEditor.experimental.useVersion2": true, } diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index 759ba84e090..55efbe60085 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -89,7 +89,6 @@ declare namespace monaco { } declare namespace monaco.editor { -#include(vs/editor/browser/widget/diffNavigator): IDiffNavigator #includeAll(vs/editor/standalone/browser/standaloneEditor;languages.Token=>Token): #include(vs/editor/standalone/common/standaloneTheme): BuiltinTheme, IStandaloneThemeData, IColors #include(vs/editor/common/languages/supports/tokenization): ITokenThemeRule diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts deleted file mode 100644 index 47dba6865b7..00000000000 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ /dev/null @@ -1,2802 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as dom from 'vs/base/browser/dom'; -import { createFastDomNode, FastDomNode } from 'vs/base/browser/fastDomNode'; -import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; -import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; -import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; -import { IBoundarySashes, ISashEvent, IVerticalSashLayoutProvider, Orientation, Sash, SashState } from 'vs/base/browser/ui/sash/sash'; -import * as assert from 'vs/base/common/assert'; -import { RunOnceScheduler } from 'vs/base/common/async'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import { Codicon } from 'vs/base/common/codicons'; -import { Color } from 'vs/base/common/color'; -import { onUnexpectedError } from 'vs/base/common/errors'; -import { Emitter, Event } from 'vs/base/common/event'; -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { Constants } from 'vs/base/common/uint'; -import { URI } from 'vs/base/common/uri'; -import 'vs/css!./media/diffEditor'; -import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; -import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; -import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; -import * as editorBrowser from 'vs/editor/browser/editorBrowser'; -import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll'; -import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; -import { DiffReview } from 'vs/editor/browser/widget/diffReview'; -import { IDiffLinesChange, InlineDiffMargin } from 'vs/editor/browser/widget/inlineDiffMargin'; -import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider'; -import { clampedFloat, clampedInt, EditorFontLigatures, EditorLayoutInfo, EditorOption, EditorOptions, IDiffEditorOptions, boolean as validateBooleanOption, stringSet as validateStringSetOption, ValidDiffEditorBaseOptions } from 'vs/editor/common/config/editorOptions'; -import { FontInfo } from 'vs/editor/common/config/fontInfo'; -import { IDimension } from 'vs/editor/common/core/dimension'; -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 { StringBuilder } from 'vs/editor/common/core/stringBuilder'; -import { IChange, ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; -import * as editorCommon from 'vs/editor/common/editorCommon'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; -import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { ILineBreaksComputer } from 'vs/editor/common/modelLineProjectionData'; -import { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; -import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { IEditorWhitespace, InlineDecoration, InlineDecorationType, IViewModel, ViewLineRenderingData } from 'vs/editor/common/viewModel'; -import { OverviewRulerZone } from 'vs/editor/common/viewModel/overviewZoneManager'; -import * as nls from 'vs/nls'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IEditorProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; -import { defaultInsertColor, defaultRemoveColor, diffDiagonalFill, diffInserted, diffOverviewRulerInserted, diffOverviewRulerRemoved, diffRemoved } from 'vs/platform/theme/common/colorRegistry'; -import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; -import { getThemeTypeSelector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; - -export interface IDiffCodeEditorWidgetOptions { - originalEditor?: ICodeEditorWidgetOptions; - modifiedEditor?: ICodeEditorWidgetOptions; -} - -interface IEditorDiffDecorations { - decorations: IModelDeltaDecoration[]; - overviewZones: OverviewRulerZone[]; -} - -interface IEditorDiffDecorationsWithZones extends IEditorDiffDecorations { - zones: IMyViewZone[]; -} - -interface IEditorsDiffDecorationsWithZones { - original: IEditorDiffDecorationsWithZones; - modified: IEditorDiffDecorationsWithZones; -} - -interface IEditorsZones { - original: IMyViewZone[]; - modified: IMyViewZone[]; -} - -class VisualEditorState { - private _zones: string[]; - private _inlineDiffMargins: InlineDiffMargin[]; - private _zonesMap: { [zoneId: string]: boolean }; - private _decorations: string[]; - - constructor( - private _contextMenuService: IContextMenuService, - private _clipboardService: IClipboardService - ) { - this._zones = []; - this._inlineDiffMargins = []; - this._zonesMap = {}; - this._decorations = []; - } - - public getForeignViewZones(allViewZones: IEditorWhitespace[]): IEditorWhitespace[] { - return allViewZones.filter((z) => !this._zonesMap[String(z.id)]); - } - - public clean(editor: CodeEditorWidget): void { - // (1) View zones - if (this._zones.length > 0) { - editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (const zoneId of this._zones) { - viewChangeAccessor.removeZone(zoneId); - } - }); - } - this._zones = []; - this._zonesMap = {}; - - // (2) Model decorations - editor.changeDecorations((changeAccessor) => { - this._decorations = changeAccessor.deltaDecorations(this._decorations, []); - }); - } - - public apply(editor: CodeEditorWidget, overviewRuler: editorBrowser.IOverviewRuler | null, newDecorations: IEditorDiffDecorationsWithZones, restoreScrollState: boolean): void { - - const scrollState = restoreScrollState ? StableEditorScrollState.capture(editor) : null; - - // view zones - editor.changeViewZones((viewChangeAccessor: editorBrowser.IViewZoneChangeAccessor) => { - for (const zoneId of this._zones) { - viewChangeAccessor.removeZone(zoneId); - } - for (const inlineDiffMargin of this._inlineDiffMargins) { - inlineDiffMargin.dispose(); - } - this._zones = []; - this._zonesMap = {}; - this._inlineDiffMargins = []; - for (let i = 0, length = newDecorations.zones.length; i < length; i++) { - const viewZone = newDecorations.zones[i]; - viewZone.suppressMouseDown = true; - viewZone.showInHiddenAreas = true; - const zoneId = viewChangeAccessor.addZone(viewZone); - this._zones.push(zoneId); - this._zonesMap[String(zoneId)] = true; - - if (newDecorations.zones[i].diff && viewZone.marginDomNode) { - viewZone.suppressMouseDown = false; - if (newDecorations.zones[i].diff?.originalModel.getValueLength() !== 0) { - // do not contribute diff margin actions for newly created files - this._inlineDiffMargins.push(new InlineDiffMargin(zoneId, viewZone.marginDomNode, editor, newDecorations.zones[i].diff!, this._contextMenuService, this._clipboardService)); - } - } - } - }); - - scrollState?.restore(editor); - - // decorations - editor.changeDecorations((changeAccessor) => { - this._decorations = changeAccessor.deltaDecorations(this._decorations, newDecorations.decorations); - }); - - // overview ruler - overviewRuler?.setZones(newDecorations.overviewZones); - } -} - -let DIFF_EDITOR_ID = 0; - - -const diffInsertIcon = registerIcon('diff-insert', Codicon.add, nls.localize('diffInsertIcon', 'Line decoration for inserts in the diff editor.')); -const diffRemoveIcon = registerIcon('diff-remove', Codicon.remove, nls.localize('diffRemoveIcon', 'Line decoration for removals in the diff editor.')); -export const diffEditorWidgetTtPolicy = createTrustedTypesPolicy('diffEditorWidget', { createHTML: value => value }); - -const ariaNavigationTip = nls.localize('diff-aria-navigation-tip', ' use Shift + F7 to navigate changes'); - -export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffEditor { - - private static readonly ONE_OVERVIEW_WIDTH = 15; - public static readonly ENTIRE_DIFF_OVERVIEW_WIDTH = 30; - private static readonly UPDATE_DIFF_DECORATIONS_DELAY = 200; // ms - - private readonly _onDidDispose: Emitter = this._register(new Emitter()); - public readonly onDidDispose: Event = this._onDidDispose.event; - - protected readonly _onDidChangeModel: Emitter = this._register(new Emitter()); - public readonly onDidChangeModel: Event = this._onDidChangeModel.event; - - private readonly _onDidUpdateDiff: Emitter = this._register(new Emitter()); - public readonly onDidUpdateDiff: Event = this._onDidUpdateDiff.event; - - private readonly _onDidContentSizeChange: Emitter = this._register(new Emitter()); - public readonly onDidContentSizeChange: Event = this._onDidContentSizeChange.event; - - private readonly _id: number; - private _state: editorBrowser.DiffEditorState; - private _updatingDiffProgress: IProgressRunner | null; - - private readonly _domElement: HTMLElement; - protected readonly _containerDomElement: HTMLElement; - private readonly _overviewDomElement: HTMLElement; - private readonly _overviewViewportDomElement: FastDomNode; - - private readonly _elementSizeObserver: ElementSizeObserver; - - private readonly _originalEditor: CodeEditorWidget; - private readonly _originalDomNode: HTMLElement; - private readonly _originalEditorState: VisualEditorState; - private _originalOverviewRuler: editorBrowser.IOverviewRuler | null; - - private readonly _modifiedEditor: CodeEditorWidget; - private readonly _modifiedDomNode: HTMLElement; - private readonly _modifiedEditorState: VisualEditorState; - private _modifiedOverviewRuler: editorBrowser.IOverviewRuler | null; - - private _currentlyChangingViewZones: boolean; - private _beginUpdateDecorationsTimeout: number; - private _diffComputationToken: number; - private _diffComputationResult: IDiffComputationResult | null; - - private _isVisible: boolean; - private _isHandlingScrollEvent: boolean; - - private _boundarySashes: IBoundarySashes | undefined; - - private _options: ValidDiffEditorBaseOptions; - - private _strategy!: DiffEditorWidgetStyle; - - private readonly _updateDecorationsRunner: RunOnceScheduler; - - private readonly _documentDiffProvider: WorkerBasedDocumentDiffProvider; - private readonly _contextKeyService: IContextKeyService; - private readonly _instantiationService: IInstantiationService; - private readonly _codeEditorService: ICodeEditorService; - private readonly _themeService: IThemeService; - private readonly _notificationService: INotificationService; - - private readonly _reviewPane: DiffReview; - - private isEmbeddedDiffEditorKey: IContextKey; - - private _diffNavigator: DiffNavigator | undefined; - - constructor( - domElement: HTMLElement, - options: Readonly, - codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, - @IClipboardService clipboardService: IClipboardService, - @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService, - @ICodeEditorService codeEditorService: ICodeEditorService, - @IThemeService themeService: IThemeService, - @INotificationService notificationService: INotificationService, - @IContextMenuService contextMenuService: IContextMenuService, - @IEditorProgressService private readonly _editorProgressService: IEditorProgressService - ) { - super(); - codeEditorService.willCreateDiffEditor(); - - this._documentDiffProvider = this._register(instantiationService.createInstance(WorkerBasedDocumentDiffProvider, options)); - this._register(this._documentDiffProvider.onDidChange(e => this._beginUpdateDecorationsSoon())); - - this._codeEditorService = codeEditorService; - this._contextKeyService = this._register(contextKeyService.createScoped(domElement)); - this._instantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this._contextKeyService])); - this._contextKeyService.createKey('isInDiffEditor', true); - this._themeService = themeService; - this._notificationService = notificationService; - - this._id = (++DIFF_EDITOR_ID); - this._state = editorBrowser.DiffEditorState.Idle; - this._updatingDiffProgress = null; - - this._domElement = domElement; - options = options || {}; - - this._options = validateDiffEditorOptions(options, { - enableSplitViewResizing: true, - splitViewDefaultRatio: 0.5, - renderSideBySide: true, - renderMarginRevertIcon: true, - maxComputationTime: 5000, - maxFileSize: 50, - ignoreTrimWhitespace: true, - renderIndicators: true, - originalEditable: false, - diffCodeLens: false, - renderOverviewRuler: true, - diffWordWrap: 'inherit', - diffAlgorithm: 'advanced', - accessibilityVerbose: false, - experimental: { - showEmptyDecorations: false, - showMoves: false, - }, - hideUnchangedRegions: { - enabled: false, - contextLineCount: 0, - minimumLineCount: 0, - revealLineCount: 0, - }, - isInEmbeddedEditor: false, - onlyShowAccessibleDiffViewer: false, - renderSideBySideInlineBreakpoint: 0, - useInlineViewWhenSpaceIsLimited: false, - }); - - this.isEmbeddedDiffEditorKey = EditorContextKeys.isEmbeddedDiffEditor.bindTo(this._contextKeyService); - this.isEmbeddedDiffEditorKey.set(typeof options.isInEmbeddedEditor !== 'undefined' ? options.isInEmbeddedEditor : false); - this._updateDecorationsRunner = this._register(new RunOnceScheduler(() => this._updateDecorations(), 0)); - - this._containerDomElement = document.createElement('div'); - this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getColorTheme(), this._options.renderSideBySide); - this._containerDomElement.style.position = 'relative'; - this._containerDomElement.style.height = '100%'; - this._domElement.appendChild(this._containerDomElement); - - this._overviewViewportDomElement = createFastDomNode(document.createElement('div')); - this._overviewViewportDomElement.setClassName('diffViewport'); - this._overviewViewportDomElement.setPosition('absolute'); - - this._overviewDomElement = document.createElement('div'); - this._overviewDomElement.className = 'diffOverview'; - this._overviewDomElement.style.position = 'absolute'; - - this._overviewDomElement.appendChild(this._overviewViewportDomElement.domNode); - - this._register(dom.addStandardDisposableListener(this._overviewDomElement, dom.EventType.POINTER_DOWN, (e) => { - this._modifiedEditor.delegateVerticalScrollbarPointerDown(e); - })); - this._register(dom.addDisposableListener(this._overviewDomElement, dom.EventType.MOUSE_WHEEL, (e: IMouseWheelEvent) => { - this._modifiedEditor.delegateScrollFromMouseWheelEvent(e); - }, { passive: false })); - if (this._options.renderOverviewRuler) { - this._containerDomElement.appendChild(this._overviewDomElement); - } - - // Create left side - this._originalDomNode = document.createElement('div'); - this._originalDomNode.className = 'editor original'; - this._originalDomNode.style.position = 'absolute'; - this._originalDomNode.style.height = '100%'; - this._containerDomElement.appendChild(this._originalDomNode); - - // Create right side - this._modifiedDomNode = document.createElement('div'); - this._modifiedDomNode.className = 'editor modified'; - this._modifiedDomNode.style.position = 'absolute'; - this._modifiedDomNode.style.height = '100%'; - this._containerDomElement.appendChild(this._modifiedDomNode); - - this._beginUpdateDecorationsTimeout = -1; - this._currentlyChangingViewZones = false; - this._diffComputationToken = 0; - - this._originalEditorState = new VisualEditorState(contextMenuService, clipboardService); - this._modifiedEditorState = new VisualEditorState(contextMenuService, clipboardService); - - this._isVisible = true; - this._isHandlingScrollEvent = false; - - this._elementSizeObserver = this._register(new ElementSizeObserver(this._containerDomElement, options.dimension)); - this._register(this._elementSizeObserver.onDidChange(() => this._onDidContainerSizeChanged())); - if (options.automaticLayout) { - this._elementSizeObserver.startObserving(); - } - - this._diffComputationResult = null; - - this._originalEditor = this._createLeftHandSideEditor(options, codeEditorWidgetOptions.originalEditor || {}); - this._modifiedEditor = this._createRightHandSideEditor(options, codeEditorWidgetOptions.modifiedEditor || {}); - - this._originalOverviewRuler = null; - this._modifiedOverviewRuler = null; - - this._reviewPane = instantiationService.createInstance(DiffReview, this); - this._containerDomElement.appendChild(this._reviewPane.domNode.domNode); - this._containerDomElement.appendChild(this._reviewPane.shadow.domNode); - this._containerDomElement.appendChild(this._reviewPane.actionBarContainer.domNode); - - if (this._options.renderSideBySide) { - this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._options.enableSplitViewResizing, this._options.splitViewDefaultRatio)); - } else { - this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._options.enableSplitViewResizing)); - } - - this._register(themeService.onDidColorThemeChange(t => { - if (this._strategy && this._strategy.applyColors(t)) { - this._updateDecorationsRunner.schedule(); - } - this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getColorTheme(), this._options.renderSideBySide); - })); - - const contributions: IDiffEditorContributionDescription[] = EditorExtensionsRegistry.getDiffEditorContributions(); - for (const desc of contributions) { - try { - this._register(instantiationService.createInstance(desc.ctor, this)); - } catch (err) { - onUnexpectedError(err); - } - } - - this._codeEditorService.addDiffEditor(this); - } - - public get ignoreTrimWhitespace(): boolean { - return this._options.ignoreTrimWhitespace; - } - - public get maxComputationTime(): number { - return this._options.maxComputationTime; - } - - public get renderSideBySide(): boolean { - return this._options.renderSideBySide; - } - - public getContentHeight(): number { - return this._modifiedEditor.getContentHeight(); - } - - public getViewWidth(): number { - return this._elementSizeObserver.getWidth(); - } - - setBoundarySashes(sashes: IBoundarySashes) { - this._boundarySashes = sashes; - this._strategy.setBoundarySashes(sashes); - } - - private _setState(newState: editorBrowser.DiffEditorState): void { - if (this._state === newState) { - return; - } - this._state = newState; - - if (this._updatingDiffProgress) { - this._updatingDiffProgress.done(); - this._updatingDiffProgress = null; - } - - if (this._state === editorBrowser.DiffEditorState.ComputingDiff) { - this._updatingDiffProgress = this._editorProgressService.show(true, 1000); - } - } - - public hasWidgetFocus(): boolean { - return dom.isAncestor(document.activeElement, this._domElement); - } - - public accessibleDiffViewerNext(): void { - this._reviewPane.next(); - } - - public accessibleDiffViewerPrev(): void { - this._reviewPane.prev(); - } - - private static _getClassName(theme: IColorTheme, renderSideBySide: boolean): string { - let result = 'monaco-diff-editor monaco-editor-background '; - if (renderSideBySide) { - result += 'side-by-side '; - } - result += getThemeTypeSelector(theme.type); - return result; - } - - private _disposeOverviewRulers(): void { - if (this._originalOverviewRuler) { - this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); - this._originalOverviewRuler.dispose(); - this._originalOverviewRuler = null; - } - if (this._modifiedOverviewRuler) { - this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); - this._modifiedOverviewRuler.dispose(); - this._modifiedOverviewRuler = null; - } - } - - private _createOverviewRulers(): void { - if (!this._options.renderOverviewRuler) { - return; - } - - assert.ok(!this._originalOverviewRuler && !this._modifiedOverviewRuler); - - if (this._originalEditor.hasModel()) { - this._originalOverviewRuler = this._originalEditor.createOverviewRuler('original diffOverviewRuler')!; - this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode()); - } - if (this._modifiedEditor.hasModel()) { - this._modifiedOverviewRuler = this._modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; - this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode()); - } - - this._layoutOverviewRulers(); - } - - private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - const editor = this._createInnerEditor(this._instantiationService, this._originalDomNode, this._adjustOptionsForLeftHandSide(options), codeEditorWidgetOptions); - - this._register(editor.onDidScrollChange((e) => { - if (this._isHandlingScrollEvent) { - return; - } - if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { - return; - } - this._isHandlingScrollEvent = true; - this._modifiedEditor.setScrollPosition({ - scrollLeft: e.scrollLeft, - scrollTop: e.scrollTop - }); - this._isHandlingScrollEvent = false; - - this._layoutOverviewViewport(); - })); - - this._register(editor.onDidChangeViewZones(() => { - this._onViewZonesChanged(); - })); - - this._register(editor.onDidChangeConfiguration((e) => { - if (!editor.getModel()) { - return; - } - if (e.hasChanged(EditorOption.fontInfo)) { - this._updateDecorationsRunner.schedule(); - } - if (e.hasChanged(EditorOption.wrappingInfo)) { - this._updateDecorationsRunner.cancel(); - this._updateDecorations(); - } - })); - - this._register(editor.onDidChangeHiddenAreas(() => { - this._updateDecorationsRunner.cancel(); - this._updateDecorations(); - })); - - this._register(editor.onDidChangeModelContent(() => { - if (this._isVisible) { - this._beginUpdateDecorationsSoon(); - } - })); - - const isInDiffLeftEditorKey = this._contextKeyService.createKey('isInDiffLeftEditor', editor.hasWidgetFocus()); - this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); - - this._register(editor.onDidContentSizeChange(e => { - const width = this._originalEditor.getContentWidth() + this._modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; - const height = Math.max(this._modifiedEditor.getContentHeight(), this._originalEditor.getContentHeight()); - - this._onDidContentSizeChange.fire({ - contentHeight: height, - contentWidth: width, - contentHeightChanged: e.contentHeightChanged, - contentWidthChanged: e.contentWidthChanged - }); - })); - - return editor; - } - - private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - const editor = this._createInnerEditor(this._instantiationService, this._modifiedDomNode, this._adjustOptionsForRightHandSide(options), codeEditorWidgetOptions); - - this._register(editor.onDidScrollChange((e) => { - if (this._isHandlingScrollEvent) { - return; - } - if (!e.scrollTopChanged && !e.scrollLeftChanged && !e.scrollHeightChanged) { - return; - } - this._isHandlingScrollEvent = true; - this._originalEditor.setScrollPosition({ - scrollLeft: e.scrollLeft, - scrollTop: e.scrollTop - }); - this._isHandlingScrollEvent = false; - - this._layoutOverviewViewport(); - })); - - this._register(editor.onDidChangeViewZones(() => { - this._onViewZonesChanged(); - })); - - this._register(editor.onDidChangeConfiguration((e) => { - if (!editor.getModel()) { - return; - } - if (e.hasChanged(EditorOption.fontInfo)) { - this._updateDecorationsRunner.schedule(); - } - if (e.hasChanged(EditorOption.wrappingInfo)) { - this._updateDecorationsRunner.cancel(); - this._updateDecorations(); - } - })); - - this._register(editor.onDidChangeHiddenAreas(() => { - this._updateDecorationsRunner.cancel(); - this._updateDecorations(); - })); - - this._register(editor.onDidChangeModelContent(() => { - if (this._isVisible) { - this._beginUpdateDecorationsSoon(); - } - })); - - this._register(editor.onDidChangeModelOptions((e) => { - if (e.tabSize) { - this._updateDecorationsRunner.schedule(); - } - })); - - const isInDiffRightEditorKey = this._contextKeyService.createKey('isInDiffRightEditor', editor.hasWidgetFocus()); - this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); - this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); - - this._register(editor.onDidContentSizeChange(e => { - const width = this._originalEditor.getContentWidth() + this._modifiedEditor.getContentWidth() + DiffEditorWidget.ONE_OVERVIEW_WIDTH; - const height = Math.max(this._modifiedEditor.getContentHeight(), this._originalEditor.getContentHeight()); - - this._onDidContentSizeChange.fire({ - contentHeight: height, - contentWidth: width, - contentHeightChanged: e.contentHeightChanged, - contentWidthChanged: e.contentWidthChanged - }); - })); - - // Revert change when an arrow is clicked. - this._register(editor.onMouseDown(event => { - if (!event.event.rightButton && event.target.position && event.target.element?.className.includes('arrow-revert-change')) { - const lineNumber = event.target.position.lineNumber; - const viewZone = event.target as editorBrowser.IMouseTargetViewZone | undefined; - const change = this._diffComputationResult?.changes.find(c => - // delete change - viewZone?.detail.afterLineNumber === c.modifiedStartLineNumber || - // other changes - (c.modifiedEndLineNumber > 0 && c.modifiedStartLineNumber === lineNumber)); - if (change) { - this.revertChange(change); - } - event.event.stopPropagation(); - this._updateDecorations(); - return; - } - })); - - return editor; - } - - /** - * Reverts a change in the modified editor. - */ - revertChange(change: IChange) { - const editor = this._modifiedEditor; - const original = this._originalEditor.getModel(); - const modified = this._modifiedEditor.getModel(); - if (!original || !modified || !editor) { - return; - } - - const originalRange = change.originalEndLineNumber > 0 ? new Range(change.originalStartLineNumber, 1, change.originalEndLineNumber, original.getLineMaxColumn(change.originalEndLineNumber)) : null; - const originalContent = originalRange ? original.getValueInRange(originalRange) : null; - - const newRange = change.modifiedEndLineNumber > 0 ? new Range(change.modifiedStartLineNumber, 1, change.modifiedEndLineNumber, modified.getLineMaxColumn(change.modifiedEndLineNumber)) : null; - - const eol = modified.getEOL(); - - if (change.originalEndLineNumber === 0 && newRange) { - // Insert change. - // To revert: delete the new content and a linebreak (if possible) - - let range = newRange; - if (change.modifiedStartLineNumber > 1) { - // Try to include a linebreak from before. - range = newRange.setStartPosition(change.modifiedStartLineNumber - 1, modified.getLineMaxColumn(change.modifiedStartLineNumber - 1)); - } else if (change.modifiedEndLineNumber < modified.getLineCount()) { - // Try to include the linebreak from after. - range = newRange.setEndPosition(change.modifiedEndLineNumber + 1, 1); - } - editor.executeEdits('diffEditor', [{ - range, - text: '', - }]); - } else if (change.modifiedEndLineNumber === 0 && originalContent !== null) { - // Delete change. - // To revert: insert the old content and a linebreak. - - const insertAt = change.modifiedStartLineNumber < modified.getLineCount() ? new Position(change.modifiedStartLineNumber + 1, 1) : new Position(change.modifiedStartLineNumber, modified.getLineMaxColumn(change.modifiedStartLineNumber)); - editor.executeEdits('diffEditor', [{ - range: Range.fromPositions(insertAt, insertAt), - text: change.modifiedStartLineNumber < modified.getLineCount() ? originalContent + eol : eol + originalContent, - }]); - } else if (newRange && originalContent !== null) { - // Modified change. - editor.executeEdits('diffEditor', [{ - range: newRange, - text: originalContent, - }]); - } - } - - protected _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { - return instantiationService.createInstance(CodeEditorWidget, container, options, editorWidgetOptions); - } - - public override dispose(): void { - this._codeEditorService.removeDiffEditor(this); - - if (this._beginUpdateDecorationsTimeout !== -1) { - window.clearTimeout(this._beginUpdateDecorationsTimeout); - this._beginUpdateDecorationsTimeout = -1; - } - - this._cleanViewZonesAndDecorations(); - - if (this._originalOverviewRuler) { - this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); - this._originalOverviewRuler.dispose(); - } - if (this._modifiedOverviewRuler) { - this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); - this._modifiedOverviewRuler.dispose(); - } - this._overviewDomElement.removeChild(this._overviewViewportDomElement.domNode); - if (this._options.renderOverviewRuler) { - this._containerDomElement.removeChild(this._overviewDomElement); - } - - this._containerDomElement.removeChild(this._originalDomNode); - this._originalEditor.dispose(); - - this._containerDomElement.removeChild(this._modifiedDomNode); - this._modifiedEditor.dispose(); - - this._strategy.dispose(); - - this._containerDomElement.removeChild(this._reviewPane.domNode.domNode); - this._containerDomElement.removeChild(this._reviewPane.shadow.domNode); - this._containerDomElement.removeChild(this._reviewPane.actionBarContainer.domNode); - this._reviewPane.dispose(); - - this._domElement.removeChild(this._containerDomElement); - - this._onDidDispose.fire(); - - super.dispose(); - } - - //------------ begin IDiffEditor methods - - public getId(): string { - return this.getEditorType() + ':' + this._id; - } - - public getEditorType(): string { - return editorCommon.EditorType.IDiffEditor; - } - - public getLineChanges(): ILineChange[] | null { - if (!this._diffComputationResult) { - return null; - } - return this._diffComputationResult.changes; - } - - public getDiffComputationResult(): IDiffComputationResult | null { - return this._diffComputationResult; - } - - public getOriginalEditor(): editorBrowser.ICodeEditor { - return this._originalEditor; - } - - public getModifiedEditor(): editorBrowser.ICodeEditor { - return this._modifiedEditor; - } - - public updateOptions(_newOptions: Readonly): void { - const newOptions = validateDiffEditorOptions(_newOptions, this._options); - const changed = changedDiffEditorOptions(this._options, newOptions); - this._options = newOptions; - - this.isEmbeddedDiffEditorKey.set(typeof _newOptions.isInEmbeddedEditor !== 'undefined' ? _newOptions.isInEmbeddedEditor : false); - - const beginUpdateDecorations = (changed.ignoreTrimWhitespace || changed.renderIndicators || changed.renderMarginRevertIcon); - const beginUpdateDecorationsSoon = (this._isVisible && (changed.maxComputationTime || changed.maxFileSize)); - this._documentDiffProvider.setOptions(newOptions); - - if (beginUpdateDecorations) { - this._beginUpdateDecorations(); - } else if (beginUpdateDecorationsSoon) { - this._beginUpdateDecorationsSoon(); - } - - this._modifiedEditor.updateOptions(this._adjustOptionsForRightHandSide(_newOptions)); - this._originalEditor.updateOptions(this._adjustOptionsForLeftHandSide(_newOptions)); - - // enableSplitViewResizing - this._strategy.setEnableSplitViewResizing(this._options.enableSplitViewResizing, this._options.splitViewDefaultRatio); - - // renderSideBySide - if (changed.renderSideBySide) { - if (this._options.renderSideBySide) { - this._setStrategy(new DiffEditorWidgetSideBySide(this._createDataSource(), this._options.enableSplitViewResizing, this._options.splitViewDefaultRatio)); - } else { - this._setStrategy(new DiffEditorWidgetInline(this._createDataSource(), this._options.enableSplitViewResizing)); - } - // Update class name - this._containerDomElement.className = DiffEditorWidget._getClassName(this._themeService.getColorTheme(), this._options.renderSideBySide); - } - - // renderOverviewRuler - if (changed.renderOverviewRuler) { - if (this._options.renderOverviewRuler) { - this._containerDomElement.appendChild(this._overviewDomElement); - } else { - this._containerDomElement.removeChild(this._overviewDomElement); - } - } - } - - public getModel(): editorCommon.IDiffEditorModel { - return { - original: this._originalEditor.getModel()!, - modified: this._modifiedEditor.getModel()! - }; - } - - public createViewModel(model: editorCommon.IDiffEditorModel): editorCommon.IDiffEditorViewModel { - return { - model, - async waitForDiff() { - // noop - }, - }; - } - - public setModel(model: editorCommon.IDiffEditorModel | editorCommon.IDiffEditorViewModel | null): void { - if (model && 'model' in model) { - model = model.model; - } - - // Guard us against partial null model - if (model && (!model.original || !model.modified)) { - throw new Error(!model.original ? 'DiffEditorWidget.setModel: Original model is null' : 'DiffEditorWidget.setModel: Modified model is null'); - } - - // Remove all view zones & decorations - this._cleanViewZonesAndDecorations(); - - this._disposeOverviewRulers(); - - // Update code editor models - this._originalEditor.setModel(model ? model.original : null); - this._modifiedEditor.setModel(model ? model.modified : null); - this._updateDecorationsRunner.cancel(); - - // this.originalEditor.onDidChangeModelOptions - - if (model) { - this._originalEditor.setScrollTop(0); - this._modifiedEditor.setScrollTop(0); - } - - // Disable any diff computations that will come in - this._diffComputationResult = null; - this._diffComputationToken++; - this._setState(editorBrowser.DiffEditorState.Idle); - - if (model) { - this._createOverviewRulers(); - - // Begin comparing - this._beginUpdateDecorations(); - } - - this._layoutOverviewViewport(); - - this._onDidChangeModel.fire(); - - // Diff navigator - this._diffNavigator = this._register(this._instantiationService.createInstance(DiffNavigator, this, { - alwaysRevealFirst: false, - findResultLoop: this.getModifiedEditor().getOption(EditorOption.find).loop - })); - } - - public getContainerDomNode(): HTMLElement { - return this._domElement; - } - - // #region editorBrowser.IDiffEditor: Delegating to modified Editor - - public getVisibleColumnFromPosition(position: IPosition): number { - return this._modifiedEditor.getVisibleColumnFromPosition(position); - } - - public getStatusbarColumn(position: IPosition): number { - return this._modifiedEditor.getStatusbarColumn(position); - } - - public getPosition(): Position | null { - return this._modifiedEditor.getPosition(); - } - - public setPosition(position: IPosition, source: string = 'api'): void { - this._modifiedEditor.setPosition(position, source); - } - - public revealLine(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLine(lineNumber, scrollType); - } - - public revealLineInCenter(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLineInCenter(lineNumber, scrollType); - } - - public revealLineInCenterIfOutsideViewport(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLineInCenterIfOutsideViewport(lineNumber, scrollType); - } - - public revealLineNearTop(lineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLineNearTop(lineNumber, scrollType); - } - - public revealPosition(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealPosition(position, scrollType); - } - - public revealPositionInCenter(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealPositionInCenter(position, scrollType); - } - - public revealPositionInCenterIfOutsideViewport(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealPositionInCenterIfOutsideViewport(position, scrollType); - } - - public revealPositionNearTop(position: IPosition, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealPositionNearTop(position, scrollType); - } - - public getSelection(): Selection | null { - return this._modifiedEditor.getSelection(); - } - - public getSelections(): Selection[] | null { - return this._modifiedEditor.getSelections(); - } - - public setSelection(range: IRange, source?: string): void; - public setSelection(editorRange: Range, source?: string): void; - public setSelection(selection: ISelection, source?: string): void; - public setSelection(editorSelection: Selection, source?: string): void; - public setSelection(something: any, source: string = 'api'): void { - this._modifiedEditor.setSelection(something, source); - } - - public setSelections(ranges: readonly ISelection[], source: string = 'api'): void { - this._modifiedEditor.setSelections(ranges, source); - } - - public revealLines(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLines(startLineNumber, endLineNumber, scrollType); - } - - public revealLinesInCenter(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLinesInCenter(startLineNumber, endLineNumber, scrollType); - } - - public revealLinesInCenterIfOutsideViewport(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLinesInCenterIfOutsideViewport(startLineNumber, endLineNumber, scrollType); - } - - public revealLinesNearTop(startLineNumber: number, endLineNumber: number, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealLinesNearTop(startLineNumber, endLineNumber, scrollType); - } - - public revealRange(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth, revealVerticalInCenter: boolean = false, revealHorizontal: boolean = true): void { - this._modifiedEditor.revealRange(range, scrollType, revealVerticalInCenter, revealHorizontal); - } - - public revealRangeInCenter(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealRangeInCenter(range, scrollType); - } - - public revealRangeInCenterIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealRangeInCenterIfOutsideViewport(range, scrollType); - } - - public revealRangeNearTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealRangeNearTop(range, scrollType); - } - - public revealRangeNearTopIfOutsideViewport(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealRangeNearTopIfOutsideViewport(range, scrollType); - } - - public revealRangeAtTop(range: IRange, scrollType: editorCommon.ScrollType = editorCommon.ScrollType.Smooth): void { - this._modifiedEditor.revealRangeAtTop(range, scrollType); - } - - public getSupportedActions(): editorCommon.IEditorAction[] { - return this._modifiedEditor.getSupportedActions(); - } - - public focus(): void { - this._modifiedEditor.focus(); - } - - public trigger(source: string | null | undefined, handlerId: string, payload: any): void { - this._modifiedEditor.trigger(source, handlerId, payload); - } - - public createDecorationsCollection(decorations?: IModelDeltaDecoration[]): editorCommon.IEditorDecorationsCollection { - return this._modifiedEditor.createDecorationsCollection(decorations); - } - - public changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any { - return this._modifiedEditor.changeDecorations(callback); - } - - // #endregion - - public saveViewState(): editorCommon.IDiffEditorViewState { - const originalViewState = this._originalEditor.saveViewState(); - const modifiedViewState = this._modifiedEditor.saveViewState(); - return { - original: originalViewState, - modified: modifiedViewState, - }; - } - - public restoreViewState(s: editorCommon.IDiffEditorViewState): void { - if (s && s.original && s.modified) { - const diffEditorState = s; - this._originalEditor.restoreViewState(diffEditorState.original); - this._modifiedEditor.restoreViewState(diffEditorState.modified); - } - } - - public layout(dimension?: IDimension): void { - this._elementSizeObserver.observe(dimension); - } - - - public hasTextFocus(): boolean { - return this._originalEditor.hasTextFocus() || this._modifiedEditor.hasTextFocus(); - } - - public onVisible(): void { - this._isVisible = true; - this._originalEditor.onVisible(); - this._modifiedEditor.onVisible(); - // Begin comparing - this._beginUpdateDecorations(); - } - - public onHide(): void { - this._isVisible = false; - this._originalEditor.onHide(); - this._modifiedEditor.onHide(); - // Remove all view zones & decorations - this._cleanViewZonesAndDecorations(); - } - - //------------ end IDiffEditor methods - - - - //------------ begin layouting methods - - private _onDidContainerSizeChanged(): void { - this._doLayout(); - } - - private _getReviewHeight(): number { - return this._reviewPane.isVisible() ? this._elementSizeObserver.getHeight() : 0; - } - - private _layoutOverviewRulers(): void { - if (!this._options.renderOverviewRuler) { - return; - } - - if (!this._originalOverviewRuler || !this._modifiedOverviewRuler) { - return; - } - const height = this._elementSizeObserver.getHeight(); - const reviewHeight = this._getReviewHeight(); - - const freeSpace = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH - 2 * DiffEditorWidget.ONE_OVERVIEW_WIDTH; - const layoutInfo = this._modifiedEditor.getLayoutInfo(); - if (layoutInfo) { - this._originalOverviewRuler.setLayout({ - top: 0, - width: DiffEditorWidget.ONE_OVERVIEW_WIDTH, - right: freeSpace + DiffEditorWidget.ONE_OVERVIEW_WIDTH, - height: (height - reviewHeight) - }); - this._modifiedOverviewRuler.setLayout({ - top: 0, - right: 0, - width: DiffEditorWidget.ONE_OVERVIEW_WIDTH, - height: (height - reviewHeight) - }); - } - } - - //------------ end layouting methods - - private _onViewZonesChanged(): void { - if (this._currentlyChangingViewZones) { - return; - } - this._updateDecorationsRunner.schedule(); - } - - private _beginUpdateDecorationsSoon(): void { - // Clear previous timeout if necessary - if (this._beginUpdateDecorationsTimeout !== -1) { - window.clearTimeout(this._beginUpdateDecorationsTimeout); - this._beginUpdateDecorationsTimeout = -1; - } - this._beginUpdateDecorationsTimeout = window.setTimeout(() => this._beginUpdateDecorations(), DiffEditorWidget.UPDATE_DIFF_DECORATIONS_DELAY); - } - - private _lastOriginalWarning: URI | null = null; - private _lastModifiedWarning: URI | null = null; - - private static _equals(a: URI | null, b: URI | null): boolean { - if (!a && !b) { - return true; - } - if (!a || !b) { - return false; - } - return (a.toString() === b.toString()); - } - - private _beginUpdateDecorations(): void { - if (this._beginUpdateDecorationsTimeout !== -1) { - // Cancel any pending requests in case this method is called directly - window.clearTimeout(this._beginUpdateDecorationsTimeout); - this._beginUpdateDecorationsTimeout = -1; - } - const currentOriginalModel = this._originalEditor.getModel(); - const currentModifiedModel = this._modifiedEditor.getModel(); - if (!currentOriginalModel || !currentModifiedModel) { - return; - } - - // Prevent old diff requests to come if a new request has been initiated - // The best method would be to call cancel on the Promise, but this is not - // yet supported, so using tokens for now. - this._diffComputationToken++; - const currentToken = this._diffComputationToken; - - const diffLimit = this._options.maxFileSize * 1024 * 1024; // MB - const canSyncModelForDiff = (model: ITextModel): boolean => { - const bufferTextLength = model.getValueLength(); - return (diffLimit === 0 || bufferTextLength <= diffLimit); - }; - - if (!canSyncModelForDiff(currentOriginalModel) || !canSyncModelForDiff(currentModifiedModel)) { - if ( - !DiffEditorWidget._equals(currentOriginalModel.uri, this._lastOriginalWarning) - || !DiffEditorWidget._equals(currentModifiedModel.uri, this._lastModifiedWarning) - ) { - this._lastOriginalWarning = currentOriginalModel.uri; - this._lastModifiedWarning = currentModifiedModel.uri; - this._notificationService.warn(nls.localize("diff.tooLarge", "Cannot compare files because one file is too large.")); - } - return; - } - - this._setState(editorBrowser.DiffEditorState.ComputingDiff); - this._documentDiffProvider.computeDiff(currentOriginalModel, currentModifiedModel, { - ignoreTrimWhitespace: this._options.ignoreTrimWhitespace, - maxComputationTimeMs: this._options.maxComputationTime, - computeMoves: false, - }, CancellationToken.None).then(result => { - if (currentToken === this._diffComputationToken - && currentOriginalModel === this._originalEditor.getModel() - && currentModifiedModel === this._modifiedEditor.getModel() - ) { - this._setState(editorBrowser.DiffEditorState.DiffComputed); - this._diffComputationResult = { - identical: result.identical, - quitEarly: result.quitEarly, - changes2: result.changes, - changes: result.changes.map(m => { - // TODO don't do this translation, but use the diff result directly - let originalStartLineNumber: number; - let originalEndLineNumber: number; - let modifiedStartLineNumber: number; - let modifiedEndLineNumber: number; - let innerChanges = m.innerChanges; - - if (m.original.isEmpty) { - // Insertion - originalStartLineNumber = m.original.startLineNumber - 1; - originalEndLineNumber = 0; - innerChanges = undefined; - } else { - originalStartLineNumber = m.original.startLineNumber; - originalEndLineNumber = m.original.endLineNumberExclusive - 1; - } - - if (m.modified.isEmpty) { - // Deletion - modifiedStartLineNumber = m.modified.startLineNumber - 1; - modifiedEndLineNumber = 0; - innerChanges = undefined; - } else { - modifiedStartLineNumber = m.modified.startLineNumber; - modifiedEndLineNumber = m.modified.endLineNumberExclusive - 1; - } - - return { - originalStartLineNumber, - originalEndLineNumber, - modifiedStartLineNumber, - modifiedEndLineNumber, - charChanges: innerChanges?.map(m => ({ - originalStartLineNumber: m.originalRange.startLineNumber, - originalStartColumn: m.originalRange.startColumn, - originalEndLineNumber: m.originalRange.endLineNumber, - originalEndColumn: m.originalRange.endColumn, - modifiedStartLineNumber: m.modifiedRange.startLineNumber, - modifiedStartColumn: m.modifiedRange.startColumn, - modifiedEndLineNumber: m.modifiedRange.endLineNumber, - modifiedEndColumn: m.modifiedRange.endColumn, - })) - }; - }) - }; - this._updateDecorationsRunner.schedule(); - this._onDidUpdateDiff.fire(); - } - }, (error) => { - if (currentToken === this._diffComputationToken - && currentOriginalModel === this._originalEditor.getModel() - && currentModifiedModel === this._modifiedEditor.getModel() - ) { - this._setState(editorBrowser.DiffEditorState.DiffComputed); - this._diffComputationResult = null; - this._updateDecorationsRunner.schedule(); - } - }); - } - - private _cleanViewZonesAndDecorations(): void { - this._originalEditorState.clean(this._originalEditor); - this._modifiedEditorState.clean(this._modifiedEditor); - } - - private _updateDecorations(): void { - if (!this._originalEditor.getModel() || !this._modifiedEditor.getModel()) { - return; - } - - const lineChanges = (this._diffComputationResult ? this._diffComputationResult.changes : []); - - const foreignOriginal = this._originalEditorState.getForeignViewZones(this._originalEditor.getWhitespaces()); - const foreignModified = this._modifiedEditorState.getForeignViewZones(this._modifiedEditor.getWhitespaces()); - - const renderMarginRevertIcon = this._options.renderMarginRevertIcon && !this._modifiedEditor.getOption(EditorOption.readOnly); - const diffDecorations = this._strategy.getEditorsDiffDecorations(lineChanges, this._options.ignoreTrimWhitespace, this._options.renderIndicators, renderMarginRevertIcon, foreignOriginal, foreignModified); - - try { - this._currentlyChangingViewZones = true; - this._originalEditorState.apply(this._originalEditor, this._originalOverviewRuler, diffDecorations.original, false); - this._modifiedEditorState.apply(this._modifiedEditor, this._modifiedOverviewRuler, diffDecorations.modified, true); - } finally { - this._currentlyChangingViewZones = false; - } - } - - private _adjustOptionsForSubEditor(options: Readonly): IEditorConstructionOptions { - const clonedOptions = { ...options }; - clonedOptions.inDiffEditor = true; - clonedOptions.automaticLayout = false; - // Clone scrollbar options before changing them - clonedOptions.scrollbar = { ...(clonedOptions.scrollbar || {}) }; - clonedOptions.scrollbar.vertical = 'visible'; - clonedOptions.folding = false; - clonedOptions.codeLens = this._options.diffCodeLens; - clonedOptions.fixedOverflowWidgets = true; - // clonedOptions.lineDecorationsWidth = '2ch'; - // Clone minimap options before changing them - clonedOptions.minimap = { ...(clonedOptions.minimap || {}) }; - clonedOptions.minimap.enabled = false; - return clonedOptions; - } - - private _adjustOptionsForLeftHandSide(options: Readonly): IEditorConstructionOptions { - const result = this._adjustOptionsForSubEditor(options); - if (!this._options.renderSideBySide) { - // never wrap hidden editor - result.wordWrapOverride1 = 'off'; - result.wordWrapOverride2 = 'off'; - result.stickyScroll = { enabled: false }; - } else { - result.wordWrapOverride1 = this._options.diffWordWrap; - } - if (options.originalAriaLabel) { - result.ariaLabel = options.originalAriaLabel; - } - this._updateAriaLabel(result); - result.readOnly = !this._options.originalEditable; - result.dropIntoEditor = { enabled: !result.readOnly }; - result.extraEditorClassName = 'original-in-monaco-diff-editor'; - return { - ...result, - dimension: { - height: 0, - width: 0 - } - }; - } - - private _updateAriaLabel(options: IEditorConstructionOptions): void { - let ariaLabel = options.ariaLabel ?? ''; - if (this._options.accessibilityVerbose) { - ariaLabel += ariaNavigationTip; - } else if (ariaLabel) { - ariaLabel = ariaLabel.replaceAll(ariaNavigationTip, ''); - } - options.ariaLabel = ariaLabel; - } - - private _adjustOptionsForRightHandSide(options: Readonly): IEditorConstructionOptions { - const result = this._adjustOptionsForSubEditor(options); - if (options.modifiedAriaLabel) { - result.ariaLabel = options.modifiedAriaLabel; - } - this._updateAriaLabel(result); - result.wordWrapOverride1 = this._options.diffWordWrap; - result.revealHorizontalRightPadding = EditorOptions.revealHorizontalRightPadding.defaultValue + DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH; - result.scrollbar!.verticalHasArrows = false; - result.extraEditorClassName = 'modified-in-monaco-diff-editor'; - return { - ...result, - dimension: { - height: 0, - width: 0 - } - }; - } - - public doLayout(): void { - this._elementSizeObserver.observe(); - this._doLayout(); - } - - private _doLayout(): void { - const width = this._elementSizeObserver.getWidth(); - const height = this._elementSizeObserver.getHeight(); - const reviewHeight = this._getReviewHeight(); - - const splitPoint = this._strategy.layout(); - - this._originalDomNode.style.width = splitPoint + 'px'; - this._originalDomNode.style.left = '0px'; - - this._modifiedDomNode.style.width = (width - splitPoint - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px'; - this._modifiedDomNode.style.left = splitPoint + 'px'; - - this._overviewDomElement.style.top = '0px'; - this._overviewDomElement.style.height = (height - reviewHeight) + 'px'; - this._overviewDomElement.style.width = DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH + 'px'; - this._overviewDomElement.style.left = (width - DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH) + 'px'; - this._overviewViewportDomElement.setWidth(DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH); - this._overviewViewportDomElement.setHeight(30); - - this._originalEditor.layout({ width: splitPoint, height: (height - reviewHeight) }); - this._modifiedEditor.layout({ width: width - splitPoint - (this._options.renderOverviewRuler ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0), height: (height - reviewHeight) }); - - if (this._originalOverviewRuler || this._modifiedOverviewRuler) { - this._layoutOverviewRulers(); - } - - this._reviewPane.layout(height - reviewHeight, width, reviewHeight); - - this._layoutOverviewViewport(); - } - - private _layoutOverviewViewport(): void { - const layout = this._computeOverviewViewport(); - if (!layout) { - this._overviewViewportDomElement.setTop(0); - this._overviewViewportDomElement.setHeight(0); - } else { - this._overviewViewportDomElement.setTop(layout.top); - this._overviewViewportDomElement.setHeight(layout.height); - } - } - - private _computeOverviewViewport(): { height: number; top: number } | null { - const layoutInfo = this._modifiedEditor.getLayoutInfo(); - if (!layoutInfo) { - return null; - } - - const scrollTop = this._modifiedEditor.getScrollTop(); - const scrollHeight = this._modifiedEditor.getScrollHeight(); - - const computedAvailableSize = Math.max(0, layoutInfo.height); - const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * 0); - const computedRatio = scrollHeight > 0 ? (computedRepresentableSize / scrollHeight) : 0; - - const computedSliderSize = Math.max(0, Math.floor(layoutInfo.height * computedRatio)); - const computedSliderPosition = Math.floor(scrollTop * computedRatio); - - return { - height: computedSliderSize, - top: computedSliderPosition - }; - } - - private _createDataSource(): IDataSource { - return { - getWidth: () => { - return this._elementSizeObserver.getWidth(); - }, - - getHeight: () => { - return (this._elementSizeObserver.getHeight() - this._getReviewHeight()); - }, - - getOptions: () => { - return { - renderOverviewRuler: this._options.renderOverviewRuler - }; - }, - - getContainerDomNode: () => { - return this._containerDomElement; - }, - - relayoutEditors: () => { - this._doLayout(); - }, - - getOriginalEditor: () => { - return this._originalEditor; - }, - - getModifiedEditor: () => { - return this._modifiedEditor; - } - }; - } - - private _setStrategy(newStrategy: DiffEditorWidgetStyle): void { - this._strategy?.dispose(); - - this._strategy = newStrategy; - - if (this._boundarySashes) { - newStrategy.setBoundarySashes(this._boundarySashes); - } - - newStrategy.applyColors(this._themeService.getColorTheme()); - - if (this._diffComputationResult) { - this._updateDecorations(); - } - - // Just do a layout, the strategy might need it - this._doLayout(); - } - - public goToDiff(target: 'previous' | 'next'): void { - if (target === 'next') { - this._diffNavigator?.next(); - } else { - this._diffNavigator?.previous(); - } - } - - public revealFirstDiff(): void { - // This is a hack, but it works. - if (this._diffNavigator) { - this._diffNavigator.revealFirst = true; - } - } -} - -interface IDataSource { - getWidth(): number; - getHeight(): number; - getOptions(): { renderOverviewRuler: boolean }; - getContainerDomNode(): HTMLElement; - relayoutEditors(): void; - - getOriginalEditor(): CodeEditorWidget; - getModifiedEditor(): CodeEditorWidget; -} - -abstract class DiffEditorWidgetStyle extends Disposable { - - protected _dataSource: IDataSource; - protected _insertColor: Color | null; - protected _removeColor: Color | null; - - constructor(dataSource: IDataSource) { - super(); - this._dataSource = dataSource; - this._insertColor = null; - this._removeColor = null; - } - - public applyColors(theme: IColorTheme): boolean { - const newInsertColor = theme.getColor(diffOverviewRulerInserted) || (theme.getColor(diffInserted) || defaultInsertColor).transparent(2); - const newRemoveColor = theme.getColor(diffOverviewRulerRemoved) || (theme.getColor(diffRemoved) || defaultRemoveColor).transparent(2); - const hasChanges = !newInsertColor.equals(this._insertColor) || !newRemoveColor.equals(this._removeColor); - this._insertColor = newInsertColor; - this._removeColor = newRemoveColor; - return hasChanges; - } - - public getEditorsDiffDecorations(lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean, originalWhitespaces: IEditorWhitespace[], modifiedWhitespaces: IEditorWhitespace[]): IEditorsDiffDecorationsWithZones { - // Get view zones - modifiedWhitespaces = modifiedWhitespaces.sort((a, b) => { - return a.afterLineNumber - b.afterLineNumber; - }); - originalWhitespaces = originalWhitespaces.sort((a, b) => { - return a.afterLineNumber - b.afterLineNumber; - }); - const zones = this._getViewZones(lineChanges, originalWhitespaces, modifiedWhitespaces, renderIndicators); - - // Get decorations & overview ruler zones - const originalDecorations = this._getOriginalEditorDecorations(zones, lineChanges, ignoreTrimWhitespace, renderIndicators); - const modifiedDecorations = this._getModifiedEditorDecorations(zones, lineChanges, ignoreTrimWhitespace, renderIndicators, renderMarginRevertIcon); - - return { - original: { - decorations: originalDecorations.decorations, - overviewZones: originalDecorations.overviewZones, - zones: zones.original - }, - modified: { - decorations: modifiedDecorations.decorations, - overviewZones: modifiedDecorations.overviewZones, - zones: zones.modified - } - }; - } - - protected abstract _getViewZones(lineChanges: ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], renderIndicators: boolean): IEditorsZones; - protected abstract _getOriginalEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations; - protected abstract _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean): IEditorDiffDecorations; - - public abstract setEnableSplitViewResizing(enableSplitViewResizing: boolean, defaultRatio: number): void; - public abstract layout(): number; - - setBoundarySashes(_sashes: IBoundarySashes): void { - // To be implemented by subclasses - } -} - -interface IMyViewZone { - shouldNotShrink?: boolean; - afterLineNumber: number; - afterColumn?: number; - heightInLines: number; - minWidthInPx?: number; - domNode: HTMLElement | null; - marginDomNode?: HTMLElement | null; - diff?: IDiffLinesChange; -} - -class ForeignViewZonesIterator { - - private _index: number; - private readonly _source: IEditorWhitespace[]; - public current: IEditorWhitespace | null; - - constructor(source: IEditorWhitespace[]) { - this._source = source; - this._index = -1; - this.current = null; - this.advance(); - } - - public advance(): void { - this._index++; - if (this._index < this._source.length) { - this.current = this._source[this._index]; - } else { - this.current = null; - } - } -} - -abstract class ViewZonesComputer { - - constructor( - private readonly _lineChanges: ILineChange[], - private readonly _originalForeignVZ: IEditorWhitespace[], - private readonly _modifiedForeignVZ: IEditorWhitespace[], - protected readonly _originalEditor: CodeEditorWidget, - protected readonly _modifiedEditor: CodeEditorWidget - ) { - } - - private static _getViewLineCount(editor: CodeEditorWidget, startLineNumber: number, endLineNumber: number): number { - const model = editor.getModel(); - const viewModel = editor._getViewModel(); - if (model && viewModel) { - const viewRange = getViewRange(model, viewModel, startLineNumber, endLineNumber); - return (viewRange.endLineNumber - viewRange.startLineNumber + 1); - } - - return (endLineNumber - startLineNumber + 1); - } - - public getViewZones(): IEditorsZones { - const originalLineHeight = this._originalEditor.getOption(EditorOption.lineHeight); - const modifiedLineHeight = this._modifiedEditor.getOption(EditorOption.lineHeight); - const originalHasWrapping = (this._originalEditor.getOption(EditorOption.wrappingInfo).wrappingColumn !== -1); - const modifiedHasWrapping = (this._modifiedEditor.getOption(EditorOption.wrappingInfo).wrappingColumn !== -1); - const hasWrapping = (originalHasWrapping || modifiedHasWrapping); - const originalModel = this._originalEditor.getModel()!; - const originalCoordinatesConverter = this._originalEditor._getViewModel()!.coordinatesConverter; - const modifiedCoordinatesConverter = this._modifiedEditor._getViewModel()!.coordinatesConverter; - - const result: { original: IMyViewZone[]; modified: IMyViewZone[] } = { - original: [], - modified: [] - }; - - let lineChangeModifiedLength: number = 0; - let lineChangeOriginalLength: number = 0; - let originalEquivalentLineNumber: number = 0; - let modifiedEquivalentLineNumber: number = 0; - let originalEndEquivalentLineNumber: number = 0; - let modifiedEndEquivalentLineNumber: number = 0; - - const sortMyViewZones = (a: IMyViewZone, b: IMyViewZone) => { - return a.afterLineNumber - b.afterLineNumber; - }; - - const addAndCombineIfPossible = (destination: IMyViewZone[], item: IMyViewZone) => { - if (item.domNode === null && destination.length > 0) { - const lastItem = destination[destination.length - 1]; - if (lastItem.afterLineNumber === item.afterLineNumber && lastItem.domNode === null) { - lastItem.heightInLines += item.heightInLines; - return; - } - } - destination.push(item); - }; - - const modifiedForeignVZ = new ForeignViewZonesIterator(this._modifiedForeignVZ); - const originalForeignVZ = new ForeignViewZonesIterator(this._originalForeignVZ); - - let lastOriginalLineNumber = 1; - let lastModifiedLineNumber = 1; - - // In order to include foreign view zones after the last line change, the for loop will iterate once more after the end of the `lineChanges` array - for (let i = 0, length = this._lineChanges.length; i <= length; i++) { - const lineChange = (i < length ? this._lineChanges[i] : null); - - if (lineChange !== null) { - originalEquivalentLineNumber = lineChange.originalStartLineNumber + (lineChange.originalEndLineNumber > 0 ? -1 : 0); - modifiedEquivalentLineNumber = lineChange.modifiedStartLineNumber + (lineChange.modifiedEndLineNumber > 0 ? -1 : 0); - lineChangeOriginalLength = (lineChange.originalEndLineNumber > 0 ? ViewZonesComputer._getViewLineCount(this._originalEditor, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber) : 0); - lineChangeModifiedLength = (lineChange.modifiedEndLineNumber > 0 ? ViewZonesComputer._getViewLineCount(this._modifiedEditor, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber) : 0); - originalEndEquivalentLineNumber = Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); - modifiedEndEquivalentLineNumber = Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); - } else { - // Increase to very large value to get the producing tests of foreign view zones running - originalEquivalentLineNumber += 10000000 + lineChangeOriginalLength; - modifiedEquivalentLineNumber += 10000000 + lineChangeModifiedLength; - originalEndEquivalentLineNumber = originalEquivalentLineNumber; - modifiedEndEquivalentLineNumber = modifiedEquivalentLineNumber; - } - - // Each step produces view zones, and after producing them, we try to cancel them out, to avoid empty-empty view zone cases - let stepOriginal: IMyViewZone[] = []; - let stepModified: IMyViewZone[] = []; - - // ---------------------------- PRODUCE VIEW ZONES - - // [PRODUCE] View zones due to line mapping differences (equal lines but wrapped differently) - if (hasWrapping) { - let count: number; - if (lineChange) { - if (lineChange.originalEndLineNumber > 0) { - count = lineChange.originalStartLineNumber - lastOriginalLineNumber; - } else { - count = lineChange.modifiedStartLineNumber - lastModifiedLineNumber; - } - } else { - // `lastOriginalLineNumber` has not been looked at yet - count = originalModel.getLineCount() - lastOriginalLineNumber + 1; - } - - for (let i = 0; i < count; i++) { - const originalLineNumber = lastOriginalLineNumber + i; - const modifiedLineNumber = lastModifiedLineNumber + i; - - const originalViewLineCount = originalCoordinatesConverter.getModelLineViewLineCount(originalLineNumber); - const modifiedViewLineCount = modifiedCoordinatesConverter.getModelLineViewLineCount(modifiedLineNumber); - - if (originalViewLineCount < modifiedViewLineCount) { - stepOriginal.push({ - afterLineNumber: originalLineNumber, - heightInLines: modifiedViewLineCount - originalViewLineCount, - domNode: null, - marginDomNode: null - }); - } else if (originalViewLineCount > modifiedViewLineCount) { - stepModified.push({ - afterLineNumber: modifiedLineNumber, - heightInLines: originalViewLineCount - modifiedViewLineCount, - domNode: null, - marginDomNode: null - }); - } - } - if (lineChange) { - lastOriginalLineNumber = (lineChange.originalEndLineNumber > 0 ? lineChange.originalEndLineNumber : lineChange.originalStartLineNumber) + 1; - lastModifiedLineNumber = (lineChange.modifiedEndLineNumber > 0 ? lineChange.modifiedEndLineNumber : lineChange.modifiedStartLineNumber) + 1; - } - } - - // [PRODUCE] View zone(s) in original-side due to foreign view zone(s) in modified-side - while (modifiedForeignVZ.current && modifiedForeignVZ.current.afterLineNumber <= modifiedEndEquivalentLineNumber) { - let viewZoneLineNumber: number; - if (modifiedForeignVZ.current.afterLineNumber <= modifiedEquivalentLineNumber) { - viewZoneLineNumber = originalEquivalentLineNumber - modifiedEquivalentLineNumber + modifiedForeignVZ.current.afterLineNumber; - } else { - viewZoneLineNumber = originalEndEquivalentLineNumber; - } - - let marginDomNode: HTMLDivElement | null = null; - if (lineChange && lineChange.modifiedStartLineNumber <= modifiedForeignVZ.current.afterLineNumber && modifiedForeignVZ.current.afterLineNumber <= lineChange.modifiedEndLineNumber) { - marginDomNode = this._createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(); - } - - stepOriginal.push({ - afterLineNumber: viewZoneLineNumber, - heightInLines: modifiedForeignVZ.current.height / modifiedLineHeight, - domNode: null, - marginDomNode: marginDomNode - }); - modifiedForeignVZ.advance(); - } - - // [PRODUCE] View zone(s) in modified-side due to foreign view zone(s) in original-side - while (originalForeignVZ.current && originalForeignVZ.current.afterLineNumber <= originalEndEquivalentLineNumber) { - let viewZoneLineNumber: number; - if (originalForeignVZ.current.afterLineNumber <= originalEquivalentLineNumber) { - viewZoneLineNumber = modifiedEquivalentLineNumber - originalEquivalentLineNumber + originalForeignVZ.current.afterLineNumber; - } else { - viewZoneLineNumber = modifiedEndEquivalentLineNumber; - } - stepModified.push({ - afterLineNumber: viewZoneLineNumber, - heightInLines: originalForeignVZ.current.height / originalLineHeight, - domNode: null - }); - originalForeignVZ.advance(); - } - - if (lineChange !== null && isChangeOrInsert(lineChange)) { - const r = this._produceOriginalFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); - if (r) { - stepOriginal.push(r); - } - } - - if (lineChange !== null && isChangeOrDelete(lineChange)) { - const r = this._produceModifiedFromDiff(lineChange, lineChangeOriginalLength, lineChangeModifiedLength); - if (r) { - stepModified.push(r); - } - } - - // ---------------------------- END PRODUCE VIEW ZONES - - - // ---------------------------- EMIT MINIMAL VIEW ZONES - - // [CANCEL & EMIT] Try to cancel view zones out - let stepOriginalIndex = 0; - let stepModifiedIndex = 0; - - stepOriginal = stepOriginal.sort(sortMyViewZones); - stepModified = stepModified.sort(sortMyViewZones); - - while (stepOriginalIndex < stepOriginal.length && stepModifiedIndex < stepModified.length) { - const original = stepOriginal[stepOriginalIndex]; - const modified = stepModified[stepModifiedIndex]; - - const originalDelta = original.afterLineNumber - originalEquivalentLineNumber; - const modifiedDelta = modified.afterLineNumber - modifiedEquivalentLineNumber; - - if (originalDelta < modifiedDelta) { - addAndCombineIfPossible(result.original, original); - stepOriginalIndex++; - } else if (modifiedDelta < originalDelta) { - addAndCombineIfPossible(result.modified, modified); - stepModifiedIndex++; - } else if (original.shouldNotShrink) { - addAndCombineIfPossible(result.original, original); - stepOriginalIndex++; - } else if (modified.shouldNotShrink) { - addAndCombineIfPossible(result.modified, modified); - stepModifiedIndex++; - } else { - if (original.heightInLines >= modified.heightInLines) { - // modified view zone gets removed - original.heightInLines -= modified.heightInLines; - stepModifiedIndex++; - } else { - // original view zone gets removed - modified.heightInLines -= original.heightInLines; - stepOriginalIndex++; - } - } - } - - // [EMIT] Remaining original view zones - while (stepOriginalIndex < stepOriginal.length) { - addAndCombineIfPossible(result.original, stepOriginal[stepOriginalIndex]); - stepOriginalIndex++; - } - - // [EMIT] Remaining modified view zones - while (stepModifiedIndex < stepModified.length) { - addAndCombineIfPossible(result.modified, stepModified[stepModifiedIndex]); - stepModifiedIndex++; - } - - // ---------------------------- END EMIT MINIMAL VIEW ZONES - } - - return { - original: ViewZonesComputer._ensureDomNodes(result.original), - modified: ViewZonesComputer._ensureDomNodes(result.modified), - }; - } - - private static _ensureDomNodes(zones: IMyViewZone[]): IMyViewZone[] { - return zones.map((z) => { - if (!z.domNode) { - z.domNode = createFakeLinesDiv(); - } - return z; - }); - } - - protected abstract _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null; - - protected abstract _produceOriginalFromDiff(lineChange: ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null; - - protected abstract _produceModifiedFromDiff(lineChange: ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null; -} - -function createDecoration(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, options: ModelDecorationOptions) { - return { - range: new Range(startLineNumber, startColumn, endLineNumber, endColumn), - options: options - }; -} - -const enum DiffEditorLineClasses { - Insert = 'line-insert', - Delete = 'line-delete' -} - -const DECORATIONS = { - - arrowRevertChange: ModelDecorationOptions.register({ - description: 'diff-editor-arrow-revert-change', - glyphMarginHoverMessage: new MarkdownString(undefined, { isTrusted: true, supportThemeIcons: true }).appendMarkdown(nls.localize('revertChangeHoverMessage', 'Click to revert change')), - glyphMarginClassName: 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight), - zIndex: 10001, - }), - - charDelete: ModelDecorationOptions.register({ - description: 'diff-editor-char-delete', - className: 'char-delete' - }), - charDeleteWholeLine: ModelDecorationOptions.register({ - description: 'diff-editor-char-delete-whole-line', - className: 'char-delete', - isWholeLine: true - }), - - charInsert: ModelDecorationOptions.register({ - description: 'diff-editor-char-insert', - className: 'char-insert' - }), - charInsertWholeLine: ModelDecorationOptions.register({ - description: 'diff-editor-char-insert-whole-line', - className: 'char-insert', - isWholeLine: true - }), - - lineInsert: ModelDecorationOptions.register({ - description: 'diff-editor-line-insert', - className: DiffEditorLineClasses.Insert, - marginClassName: 'gutter-insert', - isWholeLine: true - }), - lineInsertWithSign: ModelDecorationOptions.register({ - description: 'diff-editor-line-insert-with-sign', - className: DiffEditorLineClasses.Insert, - linesDecorationsClassName: 'insert-sign ' + ThemeIcon.asClassName(diffInsertIcon), - marginClassName: 'gutter-insert', - isWholeLine: true - }), - - lineDelete: ModelDecorationOptions.register({ - description: 'diff-editor-line-delete', - className: DiffEditorLineClasses.Delete, - marginClassName: 'gutter-delete', - isWholeLine: true - }), - lineDeleteWithSign: ModelDecorationOptions.register({ - description: 'diff-editor-line-delete-with-sign', - className: DiffEditorLineClasses.Delete, - linesDecorationsClassName: 'delete-sign ' + ThemeIcon.asClassName(diffRemoveIcon), - marginClassName: 'gutter-delete', - isWholeLine: true - - }), - lineDeleteMargin: ModelDecorationOptions.register({ - description: 'diff-editor-line-delete-margin', - marginClassName: 'gutter-delete', - }) - -}; - -class DiffEditorWidgetSideBySide extends DiffEditorWidgetStyle implements IVerticalSashLayoutProvider { - - static readonly MINIMUM_EDITOR_WIDTH = 100; - - private _disableSash: boolean; - private readonly _sash: Sash; - private _defaultRatio: number; - private _sashRatio: number | null; - private _sashPosition: number | null; - private _startSashPosition: number | null; - - constructor(dataSource: IDataSource, enableSplitViewResizing: boolean, defaultSashRatio: number) { - super(dataSource); - - this._disableSash = (enableSplitViewResizing === false); - this._defaultRatio = defaultSashRatio; - this._sashRatio = null; - this._sashPosition = null; - this._startSashPosition = null; - this._sash = this._register(new Sash(this._dataSource.getContainerDomNode(), this, { orientation: Orientation.VERTICAL })); - - if (this._disableSash) { - this._sash.state = SashState.Disabled; - } - - this._sash.onDidStart(() => this._onSashDragStart()); - this._sash.onDidChange((e: ISashEvent) => this._onSashDrag(e)); - this._sash.onDidEnd(() => this._onSashDragEnd()); - this._sash.onDidReset(() => this._onSashReset()); - } - - public setEnableSplitViewResizing(enableSplitViewResizing: boolean, defaultRatio: number): void { - this._defaultRatio = defaultRatio; - const newDisableSash = (enableSplitViewResizing === false); - if (this._disableSash !== newDisableSash) { - this._disableSash = newDisableSash; - this._sash.state = this._disableSash ? SashState.Disabled : SashState.Enabled; - } - } - - public layout(sashRatio: number | null = this._sashRatio || this._defaultRatio): number { - const w = this._dataSource.getWidth(); - const contentWidth = w - (this._dataSource.getOptions().renderOverviewRuler ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0); - - let sashPosition = Math.floor((sashRatio || this._defaultRatio) * contentWidth); - const midPoint = Math.floor(this._defaultRatio * contentWidth); - - sashPosition = this._disableSash ? midPoint : sashPosition || midPoint; - - if (contentWidth > DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH * 2) { - if (sashPosition < DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) { - sashPosition = DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH; - } - - if (sashPosition > contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH) { - sashPosition = contentWidth - DiffEditorWidgetSideBySide.MINIMUM_EDITOR_WIDTH; - } - } else { - sashPosition = midPoint; - } - - if (this._sashPosition !== sashPosition) { - this._sashPosition = sashPosition; - } - this._sash.layout(); - - return this._sashPosition; - } - - private _onSashDragStart(): void { - this._startSashPosition = this._sashPosition!; - } - - private _onSashDrag(e: ISashEvent): void { - const w = this._dataSource.getWidth(); - const contentWidth = w - (this._dataSource.getOptions().renderOverviewRuler ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0); - const sashPosition = this.layout((this._startSashPosition! + (e.currentX - e.startX)) / contentWidth); - - this._sashRatio = sashPosition / contentWidth; - - this._dataSource.relayoutEditors(); - } - - private _onSashDragEnd(): void { - this._sash.layout(); - } - - private _onSashReset(): void { - this._sashRatio = this._defaultRatio; - this._dataSource.relayoutEditors(); - this._sash.layout(); - } - - public getVerticalSashTop(sash: Sash): number { - return 0; - } - - public getVerticalSashLeft(sash: Sash): number { - return this._sashPosition!; - } - - public getVerticalSashHeight(sash: Sash): number { - return this._dataSource.getHeight(); - } - - override setBoundarySashes(sashes: IBoundarySashes) { - this._sash.orthogonalEndSash = sashes.bottom; - } - - protected _getViewZones(lineChanges: ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[]): IEditorsZones { - const originalEditor = this._dataSource.getOriginalEditor(); - const modifiedEditor = this._dataSource.getModifiedEditor(); - const c = new SideBySideViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); - return c.getViewZones(); - } - - protected _getOriginalEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { - const originalEditor = this._dataSource.getOriginalEditor(); - const overviewZoneColor = String(this._removeColor); - - const result: IEditorDiffDecorations = { - decorations: [], - overviewZones: [] - }; - - const originalModel = originalEditor.getModel()!; - const originalViewModel = originalEditor._getViewModel()!; - - for (const lineChange of lineChanges) { - - if (isChangeOrDelete(lineChange)) { - result.decorations.push({ - range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), - options: (renderIndicators ? DECORATIONS.lineDeleteWithSign : DECORATIONS.lineDelete) - }); - if (!isChangeOrInsert(lineChange) || !lineChange.charChanges) { - result.decorations.push(createDecoration(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charDeleteWholeLine)); - } - - const viewRange = getViewRange(originalModel, originalViewModel, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); - result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, /*use endLineNumber*/0, overviewZoneColor)); - - if (lineChange.charChanges) { - for (const charChange of lineChange.charChanges) { - if (isCharChangeOrDelete(charChange)) { - if (ignoreTrimWhitespace) { - for (let lineNumber = charChange.originalStartLineNumber; lineNumber <= charChange.originalEndLineNumber; lineNumber++) { - let startColumn: number; - let endColumn: number; - if (lineNumber === charChange.originalStartLineNumber) { - startColumn = charChange.originalStartColumn; - } else { - startColumn = originalModel.getLineFirstNonWhitespaceColumn(lineNumber); - } - if (lineNumber === charChange.originalEndLineNumber) { - endColumn = charChange.originalEndColumn; - } else { - endColumn = originalModel.getLineLastNonWhitespaceColumn(lineNumber); - } - result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charDelete)); - } - } else { - result.decorations.push(createDecoration(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn, DECORATIONS.charDelete)); - } - } - } - } - } - } - - return result; - } - - protected _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean): IEditorDiffDecorations { - const modifiedEditor = this._dataSource.getModifiedEditor(); - const overviewZoneColor = String(this._insertColor); - - const result: IEditorDiffDecorations = { - decorations: [], - overviewZones: [] - }; - - const modifiedModel = modifiedEditor.getModel()!; - const modifiedViewModel = modifiedEditor._getViewModel()!; - - for (const lineChange of lineChanges) { - - // Arrows for reverting changes. - if (renderMarginRevertIcon) { - if (lineChange.modifiedEndLineNumber > 0) { - result.decorations.push({ - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedStartLineNumber, 1), - options: DECORATIONS.arrowRevertChange - }); - } else { - const viewZone = zones.modified.find(z => z.afterLineNumber === lineChange.modifiedStartLineNumber); - if (viewZone) { - viewZone.marginDomNode = createViewZoneMarginArrow(); - } - } - } - - if (isChangeOrInsert(lineChange)) { - - result.decorations.push({ - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), - options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) - }); - if (!isChangeOrDelete(lineChange) || !lineChange.charChanges) { - result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charInsertWholeLine)); - } - - const viewRange = getViewRange(modifiedModel, modifiedViewModel, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); - result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber,/*use endLineNumber*/0, overviewZoneColor)); - - if (lineChange.charChanges) { - for (const charChange of lineChange.charChanges) { - if (isCharChangeOrInsert(charChange)) { - if (ignoreTrimWhitespace) { - for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { - let startColumn: number; - let endColumn: number; - if (lineNumber === charChange.modifiedStartLineNumber) { - startColumn = charChange.modifiedStartColumn; - } else { - startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber); - } - if (lineNumber === charChange.modifiedEndLineNumber) { - endColumn = charChange.modifiedEndColumn; - } else { - endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber); - } - result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert)); - } - } else { - result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert)); - } - } - } - } - - } - } - return result; - } -} - -class SideBySideViewZonesComputer extends ViewZonesComputer { - - constructor( - lineChanges: ILineChange[], - originalForeignVZ: IEditorWhitespace[], - modifiedForeignVZ: IEditorWhitespace[], - originalEditor: CodeEditorWidget, - modifiedEditor: CodeEditorWidget, - ) { - super(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); - } - - protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { - return null; - } - - protected _produceOriginalFromDiff(lineChange: ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - if (lineChangeModifiedLength > lineChangeOriginalLength) { - return { - afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber), - heightInLines: (lineChangeModifiedLength - lineChangeOriginalLength), - domNode: null - }; - } - return null; - } - - protected _produceModifiedFromDiff(lineChange: ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - if (lineChangeOriginalLength > lineChangeModifiedLength) { - return { - afterLineNumber: Math.max(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber), - heightInLines: (lineChangeOriginalLength - lineChangeModifiedLength), - domNode: null - }; - } - return null; - } -} - -class DiffEditorWidgetInline extends DiffEditorWidgetStyle { - - private _decorationsLeft: number; - - constructor(dataSource: IDataSource, enableSplitViewResizing: boolean) { - super(dataSource); - - this._decorationsLeft = dataSource.getOriginalEditor().getLayoutInfo().decorationsLeft; - - this._register(dataSource.getOriginalEditor().onDidLayoutChange((layoutInfo: EditorLayoutInfo) => { - if (this._decorationsLeft !== layoutInfo.decorationsLeft) { - this._decorationsLeft = layoutInfo.decorationsLeft; - dataSource.relayoutEditors(); - } - })); - } - - public setEnableSplitViewResizing(enableSplitViewResizing: boolean): void { - // Nothing to do.. - } - - protected _getViewZones(lineChanges: ILineChange[], originalForeignVZ: IEditorWhitespace[], modifiedForeignVZ: IEditorWhitespace[], renderIndicators: boolean): IEditorsZones { - const originalEditor = this._dataSource.getOriginalEditor(); - const modifiedEditor = this._dataSource.getModifiedEditor(); - const computer = new InlineViewZonesComputer(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor, renderIndicators); - return computer.getViewZones(); - } - - protected _getOriginalEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean): IEditorDiffDecorations { - const overviewZoneColor = String(this._removeColor); - - const result: IEditorDiffDecorations = { - decorations: [], - overviewZones: [] - }; - - const originalEditor = this._dataSource.getOriginalEditor(); - const originalModel = originalEditor.getModel()!; - const originalViewModel = originalEditor._getViewModel()!; - let zoneIndex = 0; - - for (const lineChange of lineChanges) { - - // Add overview zones in the overview ruler - if (isChangeOrDelete(lineChange)) { - result.decorations.push({ - range: new Range(lineChange.originalStartLineNumber, 1, lineChange.originalEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), - options: DECORATIONS.lineDeleteMargin - }); - - while (zoneIndex < zones.modified.length) { - const zone = zones.modified[zoneIndex]; - if (zone.diff && zone.diff.originalStartLineNumber >= lineChange.originalStartLineNumber) { - break; - } - zoneIndex++; - } - - let zoneHeightInLines = 0; - if (zoneIndex < zones.modified.length) { - const zone = zones.modified[zoneIndex]; - if ( - zone.diff - && zone.diff.originalStartLineNumber === lineChange.originalStartLineNumber - && zone.diff.originalEndLineNumber === lineChange.originalEndLineNumber - && zone.diff.modifiedStartLineNumber === lineChange.modifiedStartLineNumber - && zone.diff.modifiedEndLineNumber === lineChange.modifiedEndLineNumber - ) { - zoneHeightInLines = zone.heightInLines; - } - } - - const viewRange = getViewRange(originalModel, originalViewModel, lineChange.originalStartLineNumber, lineChange.originalEndLineNumber); - result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, zoneHeightInLines, overviewZoneColor)); - } - } - - return result; - } - - protected _getModifiedEditorDecorations(zones: IEditorsZones, lineChanges: ILineChange[], ignoreTrimWhitespace: boolean, renderIndicators: boolean, renderMarginRevertIcon: boolean): IEditorDiffDecorations { - const modifiedEditor = this._dataSource.getModifiedEditor(); - const overviewZoneColor = String(this._insertColor); - - const result: IEditorDiffDecorations = { - decorations: [], - overviewZones: [] - }; - - const modifiedModel = modifiedEditor.getModel()!; - const modifiedViewModel = modifiedEditor._getViewModel()!; - - for (const lineChange of lineChanges) { - - // Add decorations & overview zones - if (isChangeOrInsert(lineChange)) { - result.decorations.push({ - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER), - options: (renderIndicators ? DECORATIONS.lineInsertWithSign : DECORATIONS.lineInsert) - }); - - const viewRange = getViewRange(modifiedModel, modifiedViewModel, lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber); - result.overviewZones.push(new OverviewRulerZone(viewRange.startLineNumber, viewRange.endLineNumber, /*use endLineNumber*/0, overviewZoneColor)); - - if (lineChange.charChanges) { - for (const charChange of lineChange.charChanges) { - if (isCharChangeOrInsert(charChange)) { - if (ignoreTrimWhitespace) { - for (let lineNumber = charChange.modifiedStartLineNumber; lineNumber <= charChange.modifiedEndLineNumber; lineNumber++) { - let startColumn: number; - let endColumn: number; - if (lineNumber === charChange.modifiedStartLineNumber) { - startColumn = charChange.modifiedStartColumn; - } else { - startColumn = modifiedModel.getLineFirstNonWhitespaceColumn(lineNumber); - } - if (lineNumber === charChange.modifiedEndLineNumber) { - endColumn = charChange.modifiedEndColumn; - } else { - endColumn = modifiedModel.getLineLastNonWhitespaceColumn(lineNumber); - } - result.decorations.push(createDecoration(lineNumber, startColumn, lineNumber, endColumn, DECORATIONS.charInsert)); - } - } else { - result.decorations.push(createDecoration(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn, DECORATIONS.charInsert)); - } - } - } - } else { - result.decorations.push(createDecoration(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber, Constants.MAX_SAFE_SMALL_INTEGER, DECORATIONS.charInsertWholeLine)); - } - } - } - - return result; - } - - public layout(): number { - // An editor should not be smaller than 5px - return Math.max(5, this._decorationsLeft); - } - -} - -interface InlineModifiedViewZone extends IMyViewZone { - shouldNotShrink: boolean; - afterLineNumber: number; - heightInLines: number; - minWidthInPx: number; - domNode: HTMLElement; - marginDomNode: HTMLElement; - diff: IDiffLinesChange; -} - -class InlineViewZonesComputer extends ViewZonesComputer { - - private readonly _originalModel: ITextModel; - private readonly _renderIndicators: boolean; - private readonly _pendingLineChange: ILineChange[]; - private readonly _pendingViewZones: InlineModifiedViewZone[]; - private readonly _lineBreaksComputer: ILineBreaksComputer; - - constructor( - lineChanges: ILineChange[], - originalForeignVZ: IEditorWhitespace[], - modifiedForeignVZ: IEditorWhitespace[], - originalEditor: CodeEditorWidget, - modifiedEditor: CodeEditorWidget, - renderIndicators: boolean - ) { - super(lineChanges, originalForeignVZ, modifiedForeignVZ, originalEditor, modifiedEditor); - this._originalModel = originalEditor.getModel()!; - this._renderIndicators = renderIndicators; - this._pendingLineChange = []; - this._pendingViewZones = []; - this._lineBreaksComputer = this._modifiedEditor._getViewModel()!.createLineBreaksComputer(); - } - - public override getViewZones(): IEditorsZones { - const result = super.getViewZones(); - this._finalize(result); - return result; - } - - protected _createOriginalMarginDomNodeForModifiedForeignViewZoneInAddedRegion(): HTMLDivElement | null { - const result = document.createElement('div'); - result.className = 'inline-added-margin-view-zone'; - return result; - } - - protected _produceOriginalFromDiff(lineChange: ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - const marginDomNode = document.createElement('div'); - marginDomNode.className = 'inline-added-margin-view-zone'; - - return { - afterLineNumber: Math.max(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber), - heightInLines: lineChangeModifiedLength, - domNode: document.createElement('div'), - marginDomNode: marginDomNode - }; - } - - protected _produceModifiedFromDiff(lineChange: ILineChange, lineChangeOriginalLength: number, lineChangeModifiedLength: number): IMyViewZone | null { - const domNode = document.createElement('div'); - domNode.className = `view-lines line-delete ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`; - - const marginDomNode = document.createElement('div'); - marginDomNode.className = 'inline-deleted-margin-view-zone'; - - const viewZone: InlineModifiedViewZone = { - shouldNotShrink: true, - afterLineNumber: (lineChange.modifiedEndLineNumber === 0 ? lineChange.modifiedStartLineNumber : lineChange.modifiedStartLineNumber - 1), - heightInLines: lineChangeOriginalLength, - minWidthInPx: 0, - domNode: domNode, - marginDomNode: marginDomNode, - diff: { - originalStartLineNumber: lineChange.originalStartLineNumber, - originalEndLineNumber: lineChange.originalEndLineNumber, - modifiedStartLineNumber: lineChange.modifiedStartLineNumber, - modifiedEndLineNumber: lineChange.modifiedEndLineNumber, - originalModel: this._originalModel, - viewLineCounts: null, - } - }; - - for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { - this._lineBreaksComputer.addRequest(this._originalModel.getLineContent(lineNumber), null, null); - } - - this._pendingLineChange.push(lineChange); - this._pendingViewZones.push(viewZone); - - return viewZone; - } - - private _finalize(result: IEditorsZones): void { - const modifiedEditorOptions = this._modifiedEditor.getOptions(); - const tabSize = this._modifiedEditor.getModel()!.getOptions().tabSize; - const fontInfo = modifiedEditorOptions.get(EditorOption.fontInfo); - const disableMonospaceOptimizations = modifiedEditorOptions.get(EditorOption.disableMonospaceOptimizations); - const typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; - const scrollBeyondLastColumn = modifiedEditorOptions.get(EditorOption.scrollBeyondLastColumn); - const mightContainNonBasicASCII = this._originalModel.mightContainNonBasicASCII(); - const mightContainRTL = this._originalModel.mightContainRTL(); - const lineHeight = modifiedEditorOptions.get(EditorOption.lineHeight); - const layoutInfo = modifiedEditorOptions.get(EditorOption.layoutInfo); - const lineDecorationsWidth = layoutInfo.decorationsWidth; - const stopRenderingLineAfter = modifiedEditorOptions.get(EditorOption.stopRenderingLineAfter); - const renderWhitespace = modifiedEditorOptions.get(EditorOption.renderWhitespace); - const renderControlCharacters = modifiedEditorOptions.get(EditorOption.renderControlCharacters); - const fontLigatures = modifiedEditorOptions.get(EditorOption.fontLigatures); - - const lineBreaks = this._lineBreaksComputer.finalize(); - let lineBreakIndex = 0; - - for (let i = 0; i < this._pendingLineChange.length; i++) { - const lineChange = this._pendingLineChange[i]; - const viewZone = this._pendingViewZones[i]; - const domNode = viewZone.domNode; - applyFontInfo(domNode, fontInfo); - - const marginDomNode = viewZone.marginDomNode; - applyFontInfo(marginDomNode, fontInfo); - - const decorations: InlineDecoration[] = []; - if (lineChange.charChanges) { - for (const charChange of lineChange.charChanges) { - if (isCharChangeOrDelete(charChange)) { - decorations.push(new InlineDecoration( - new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), - 'char-delete', - InlineDecorationType.Regular - )); - } - } - } - const hasCharChanges = (decorations.length > 0); - - const sb = new StringBuilder(10000); - let maxCharsPerLine = 0; - let renderedLineCount = 0; - let viewLineCounts: number[] | null = null; - for (let lineNumber = lineChange.originalStartLineNumber; lineNumber <= lineChange.originalEndLineNumber; lineNumber++) { - const lineIndex = lineNumber - lineChange.originalStartLineNumber; - const lineTokens = this._originalModel.tokenization.getLineTokens(lineNumber); - const lineContent = lineTokens.getLineContent(); - const lineBreakData = lineBreaks[lineBreakIndex++]; - const actualDecorations = LineDecoration.filter(decorations, lineNumber, 1, lineContent.length + 1); - - if (lineBreakData) { - let lastBreakOffset = 0; - for (const breakOffset of lineBreakData.breakOffsets) { - const viewLineTokens = lineTokens.sliceAndInflate(lastBreakOffset, breakOffset, 0); - const viewLineContent = lineContent.substring(lastBreakOffset, breakOffset); - maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine( - renderedLineCount++, - viewLineContent, - viewLineTokens, - LineDecoration.extractWrapped(actualDecorations, lastBreakOffset, breakOffset), - hasCharChanges, - mightContainNonBasicASCII, - mightContainRTL, - fontInfo, - disableMonospaceOptimizations, - lineHeight, - lineDecorationsWidth, - stopRenderingLineAfter, - renderWhitespace, - renderControlCharacters, - fontLigatures, - tabSize, - sb, - marginDomNode - )); - lastBreakOffset = breakOffset; - } - if (!viewLineCounts) { - viewLineCounts = []; - } - // make sure all lines before this one have an entry in `viewLineCounts` - while (viewLineCounts.length < lineIndex) { - viewLineCounts[viewLineCounts.length] = 1; - } - viewLineCounts[lineIndex] = lineBreakData.breakOffsets.length; - viewZone.heightInLines += (lineBreakData.breakOffsets.length - 1); - const marginDomNode2 = document.createElement('div'); - marginDomNode2.className = 'gutter-delete'; - result.original.push({ - afterLineNumber: lineNumber, - afterColumn: 0, - heightInLines: lineBreakData.breakOffsets.length - 1, - domNode: createFakeLinesDiv(), - marginDomNode: marginDomNode2 - }); - } else { - maxCharsPerLine = Math.max(maxCharsPerLine, this._renderOriginalLine( - renderedLineCount++, - lineContent, - lineTokens, - actualDecorations, - hasCharChanges, - mightContainNonBasicASCII, - mightContainRTL, - fontInfo, - disableMonospaceOptimizations, - lineHeight, - lineDecorationsWidth, - stopRenderingLineAfter, - renderWhitespace, - renderControlCharacters, - fontLigatures, - tabSize, - sb, - marginDomNode - )); - } - } - maxCharsPerLine += scrollBeyondLastColumn; - - const html = sb.build(); - const trustedhtml = diffEditorWidgetTtPolicy ? diffEditorWidgetTtPolicy.createHTML(html) : html; - domNode.innerHTML = trustedhtml as string; - viewZone.minWidthInPx = (maxCharsPerLine * typicalHalfwidthCharacterWidth); - - if (viewLineCounts) { - // make sure all lines have an entry in `viewLineCounts` - const cnt = lineChange.originalEndLineNumber - lineChange.originalStartLineNumber; - while (viewLineCounts.length <= cnt) { - viewLineCounts[viewLineCounts.length] = 1; - } - } - viewZone.diff.viewLineCounts = viewLineCounts; - } - - result.original.sort((a, b) => { - return a.afterLineNumber - b.afterLineNumber; - }); - } - - private _renderOriginalLine( - renderedLineCount: number, - lineContent: string, - lineTokens: IViewLineTokens, - decorations: LineDecoration[], - hasCharChanges: boolean, - mightContainNonBasicASCII: boolean, - mightContainRTL: boolean, - fontInfo: FontInfo, - disableMonospaceOptimizations: boolean, - lineHeight: number, - lineDecorationsWidth: number, - stopRenderingLineAfter: number, - renderWhitespace: 'selection' | 'none' | 'boundary' | 'trailing' | 'all', - renderControlCharacters: boolean, - fontLigatures: string, - tabSize: number, - sb: StringBuilder, - marginDomNode: HTMLElement - ): number { - - sb.appendString('
'); - - const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, mightContainNonBasicASCII); - const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, mightContainRTL); - const output = renderViewLine(new RenderLineInput( - (fontInfo.isMonospace && !disableMonospaceOptimizations), - fontInfo.canUseHalfwidthRightwardsArrow, - lineContent, - false, - isBasicASCII, - containsRTL, - 0, - lineTokens, - decorations, - tabSize, - 0, - fontInfo.spaceWidth, - fontInfo.middotWidth, - fontInfo.wsmiddotWidth, - stopRenderingLineAfter, - renderWhitespace, - renderControlCharacters, - fontLigatures !== EditorFontLigatures.OFF, - null // Send no selections, original line cannot be selected - ), sb); - - sb.appendString('
'); - - if (this._renderIndicators) { - const marginElement = document.createElement('div'); - marginElement.className = `delete-sign ${ThemeIcon.asClassName(diffRemoveIcon)}`; - marginElement.setAttribute('style', `position:absolute;top:${renderedLineCount * lineHeight}px;width:${lineDecorationsWidth}px;height:${lineHeight}px;right:0;`); - marginDomNode.appendChild(marginElement); - } - - return output.characterMapping.getHorizontalOffset(output.characterMapping.length); - } -} - -function validateDiffWordWrap(value: 'off' | 'on' | 'inherit' | undefined, defaultValue: 'off' | 'on' | 'inherit'): 'off' | 'on' | 'inherit' { - return validateStringSetOption<'off' | 'on' | 'inherit'>(value, defaultValue, ['off', 'on', 'inherit']); -} - -function isChangeOrInsert(lineChange: ILineChange): boolean { - return lineChange.modifiedEndLineNumber > 0; -} - -function isChangeOrDelete(lineChange: ILineChange): boolean { - return lineChange.originalEndLineNumber > 0; -} - -function isCharChangeOrInsert(charChange: ICharChange): boolean { - if (charChange.modifiedStartLineNumber === charChange.modifiedEndLineNumber) { - return charChange.modifiedEndColumn - charChange.modifiedStartColumn > 0; - } - return charChange.modifiedEndLineNumber - charChange.modifiedStartLineNumber > 0; -} - -function isCharChangeOrDelete(charChange: ICharChange): boolean { - if (charChange.originalStartLineNumber === charChange.originalEndLineNumber) { - return charChange.originalEndColumn - charChange.originalStartColumn > 0; - } - return charChange.originalEndLineNumber - charChange.originalStartLineNumber > 0; -} - -function createFakeLinesDiv(): HTMLElement { - const r = document.createElement('div'); - r.className = 'diagonal-fill'; - return r; -} - -function createViewZoneMarginArrow(): HTMLElement { - const arrow = document.createElement('div'); - arrow.className = 'arrow-revert-change ' + ThemeIcon.asClassName(Codicon.arrowRight); - return dom.$('div', {}, arrow); -} - -function getViewRange(model: ITextModel, viewModel: IViewModel, startLineNumber: number, endLineNumber: number): Range { - const lineCount = model.getLineCount(); - startLineNumber = Math.min(lineCount, Math.max(1, startLineNumber)); - endLineNumber = Math.min(lineCount, Math.max(1, endLineNumber)); - return viewModel.coordinatesConverter.convertModelRangeToViewRange(new Range( - startLineNumber, model.getLineMinColumn(startLineNumber), - endLineNumber, model.getLineMaxColumn(endLineNumber) - )); -} - -function validateDiffEditorOptions(options: Readonly, defaults: ValidDiffEditorBaseOptions): ValidDiffEditorBaseOptions { - return { - enableSplitViewResizing: validateBooleanOption(options.enableSplitViewResizing, defaults.enableSplitViewResizing), - splitViewDefaultRatio: clampedFloat(options.splitViewDefaultRatio, 0.5, 0.1, 0.9), - renderSideBySide: validateBooleanOption(options.renderSideBySide, defaults.renderSideBySide), - renderMarginRevertIcon: validateBooleanOption(options.renderMarginRevertIcon, defaults.renderMarginRevertIcon), - maxComputationTime: clampedInt(options.maxComputationTime, defaults.maxComputationTime, 0, Constants.MAX_SAFE_SMALL_INTEGER), - maxFileSize: clampedInt(options.maxFileSize, defaults.maxFileSize, 0, Constants.MAX_SAFE_SMALL_INTEGER), - ignoreTrimWhitespace: validateBooleanOption(options.ignoreTrimWhitespace, defaults.ignoreTrimWhitespace), - renderIndicators: validateBooleanOption(options.renderIndicators, defaults.renderIndicators), - originalEditable: validateBooleanOption(options.originalEditable, defaults.originalEditable), - diffCodeLens: validateBooleanOption(options.diffCodeLens, defaults.diffCodeLens), - renderOverviewRuler: validateBooleanOption(options.renderOverviewRuler, defaults.renderOverviewRuler), - diffWordWrap: validateDiffWordWrap(options.diffWordWrap, defaults.diffWordWrap), - diffAlgorithm: validateStringSetOption(options.diffAlgorithm, defaults.diffAlgorithm, ['legacy', 'advanced'], { 'smart': 'legacy', 'experimental': 'advanced' }), - accessibilityVerbose: validateBooleanOption(options.accessibilityVerbose, defaults.accessibilityVerbose), - hideUnchangedRegions: { - enabled: false, - contextLineCount: 0, - minimumLineCount: 0, - revealLineCount: 0, - }, - experimental: { - showEmptyDecorations: false, - showMoves: false, - }, - isInEmbeddedEditor: validateBooleanOption(options.isInEmbeddedEditor, defaults.isInEmbeddedEditor), - onlyShowAccessibleDiffViewer: false, - renderSideBySideInlineBreakpoint: 0, - useInlineViewWhenSpaceIsLimited: false, - }; -} - -function changedDiffEditorOptions(a: ValidDiffEditorBaseOptions, b: ValidDiffEditorBaseOptions) { - return { - enableSplitViewResizing: (a.enableSplitViewResizing !== b.enableSplitViewResizing), - renderSideBySide: (a.renderSideBySide !== b.renderSideBySide), - renderMarginRevertIcon: (a.renderMarginRevertIcon !== b.renderMarginRevertIcon), - maxComputationTime: (a.maxComputationTime !== b.maxComputationTime), - maxFileSize: (a.maxFileSize !== b.maxFileSize), - ignoreTrimWhitespace: (a.ignoreTrimWhitespace !== b.ignoreTrimWhitespace), - renderIndicators: (a.renderIndicators !== b.renderIndicators), - originalEditable: (a.originalEditable !== b.originalEditable), - diffCodeLens: (a.diffCodeLens !== b.diffCodeLens), - renderOverviewRuler: (a.renderOverviewRuler !== b.renderOverviewRuler), - diffWordWrap: (a.diffWordWrap !== b.diffWordWrap), - diffAlgorithm: (a.diffAlgorithm !== b.diffAlgorithm), - accessibilityVerbose: (a.accessibilityVerbose !== b.accessibilityVerbose), - }; -} - -registerThemingParticipant((theme, collector) => { - const diffDiagonalFillColor = theme.getColor(diffDiagonalFill); - collector.addRule(` - .monaco-editor .diagonal-fill { - background-image: linear-gradient( - -45deg, - ${diffDiagonalFillColor} 12.5%, - #0000 12.5%, #0000 50%, - ${diffDiagonalFillColor} 50%, ${diffDiagonalFillColor} 62.5%, - #0000 62.5%, #0000 100% - ); - background-size: 8px 8px; - } - `); -}); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts index 3f45fae3b99..3b2f9b3204a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { addDisposableListener, addStandardDisposableListener, reset } from 'vs/base/browser/dom'; +import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Action } from 'vs/base/common/actions'; @@ -16,7 +17,6 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; import { applyStyle } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; -import { DiffReview } from 'vs/editor/browser/widget/diffReview'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; @@ -39,6 +39,8 @@ const accessibleDiffViewerRemoveIcon = registerIcon('diff-review-remove', Codico const accessibleDiffViewerCloseIcon = registerIcon('diff-review-close', Codicon.close, localize('accessibleDiffViewerCloseIcon', 'Icon for \'Close\' in accessible diff viewer.')); export class AccessibleDiffViewer extends Disposable { + public static _ttPolicy = createTrustedTypesPolicy('diffReview', { createHTML: value => value }); + constructor( private readonly _parentNode: HTMLElement, private readonly _visible: IObservable, @@ -590,15 +592,15 @@ class View extends Disposable { let lineContent: string; if (item.modifiedLineNumber !== undefined) { let html: string | TrustedHTML = this._getLineHtml(modifiedModel, modifiedOptions, modifiedModelOpts.tabSize, item.modifiedLineNumber, this._languageService.languageIdCodec); - if (DiffReview._ttPolicy) { - html = DiffReview._ttPolicy.createHTML(html as string); + if (AccessibleDiffViewer._ttPolicy) { + html = AccessibleDiffViewer._ttPolicy.createHTML(html as string); } cell.insertAdjacentHTML('beforeend', html as string); lineContent = modifiedModel.getLineContent(item.modifiedLineNumber); } else { let html: string | TrustedHTML = this._getLineHtml(originalModel, originalOptions, originalModelOpts.tabSize, item.originalLineNumber, this._languageService.languageIdCodec); - if (DiffReview._ttPolicy) { - html = DiffReview._ttPolicy.createHTML(html as string); + if (AccessibleDiffViewer._ttPolicy) { + html = AccessibleDiffViewer._ttPolicy.createHTML(html as string); } cell.insertAdjacentHTML('beforeend', html as string); lineContent = originalModel.getLineContent(item.originalLineNumber); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts index 2294861e7af..1166c4522b8 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts @@ -8,7 +8,6 @@ import { IObservable, IReader, autorunHandleChanges, observableFromEvent } from import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget'; import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart'; import { EditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; @@ -17,6 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DiffEditorOptions } from './diffEditorOptions'; import { ITextModel } from 'vs/editor/common/model'; +import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; export class DiffEditorEditors extends Disposable { public readonly modified: CodeEditorWidget; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index e899a379f99..264b8117601 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -15,7 +15,6 @@ import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions, IMouseTargetV import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget'; import { AccessibleDiffViewer } from 'vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer'; import { DiffEditorDecorations } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations'; import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorSash'; @@ -46,7 +45,14 @@ import { DiffEditorEditors } from './diffEditorEditors'; import { DiffEditorOptions } from './diffEditorOptions'; import { DiffEditorViewModel, DiffMapping, DiffState } from './diffEditorViewModel'; +export interface IDiffCodeEditorWidgetOptions { + originalEditor?: ICodeEditorWidgetOptions; + modifiedEditor?: ICodeEditorWidgetOptions; +} + export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { + public static ENTIRE_DIFF_OVERVIEW_WIDTH = OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH; + private readonly elements = h('div.monaco-diff-editor.side-by-side', { style: { position: 'relative', height: '100%' } }, [ h('div.noModificationsOverlay@overlay', { style: { position: 'absolute', height: '100%', visibility: 'hidden', } }, [$('span', {}, 'No Changes')]), h('div.editor.original@original', { style: { position: 'absolute', height: '100%' } }), @@ -279,6 +285,10 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { })); } + public getViewWidth(): number { + return this._rootSizeObserver.width.get(); + } + public getContentHeight() { return this._editors.modified.getContentHeight(); } diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts index b33db4762e0..f9d8b3f9a08 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { diffEditorWidgetTtPolicy } from 'vs/editor/browser/widget/diffEditorWidget'; import { EditorFontLigatures, EditorOption, FindComputedEditorOptionValueById } from 'vs/editor/common/config/editorOptions'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; @@ -15,7 +15,7 @@ import { LineDecoration } from 'vs/editor/common/viewLayout/lineDecorations'; import { RenderLineInput, renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; import { InlineDecoration, ViewLineRenderingData } from 'vs/editor/common/viewModel'; -const ttPolicy = diffEditorWidgetTtPolicy; +const ttPolicy = createTrustedTypesPolicy('diffEditorWidget', { createHTML: value => value }); export function renderLines(source: LineSource, options: RenderOptions, decorations: InlineDecoration[], domNode: HTMLElement): RenderLinesResult { applyFontInfo(domNode, options.fontInfo); diff --git a/src/vs/editor/browser/widget/diffNavigator.ts b/src/vs/editor/browser/widget/diffNavigator.ts deleted file mode 100644 index 1edce99edf6..00000000000 --- a/src/vs/editor/browser/widget/diffNavigator.ts +++ /dev/null @@ -1,278 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'vs/base/common/assert'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import * as objects from 'vs/base/common/objects'; -import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { ICursorPositionChangedEvent } from 'vs/editor/common/cursorEvents'; -import { Range } from 'vs/editor/common/core/range'; -import { ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; -import { ScrollType } from 'vs/editor/common/editorCommon'; -import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; - - -interface IDiffRange { - rhs: boolean; - range: Range; -} - -export interface Options { - followsCaret?: boolean; - ignoreCharChanges?: boolean; - alwaysRevealFirst?: boolean; - findResultLoop?: boolean; -} - -const defaultOptions: Options = { - followsCaret: true, - ignoreCharChanges: true, - alwaysRevealFirst: true, - findResultLoop: true -}; - -export interface IDiffNavigator { - canNavigate(): boolean; - next(): void; - previous(): void; - dispose(): void; -} - -/** - * Create a new diff navigator for the provided diff editor. - */ -export class DiffNavigator extends Disposable implements IDiffNavigator { - - private readonly _editor: IDiffEditor; - private readonly _options: Options; - private readonly _onDidUpdate = this._register(new Emitter()); - - readonly onDidUpdate: Event = this._onDidUpdate.event; - - private disposed: boolean; - public revealFirst: boolean; - private nextIdx: number; - private ranges: IDiffRange[]; - private ignoreSelectionChange: boolean; - - constructor( - editor: IDiffEditor, - options: Options = {}, - @IAudioCueService private readonly _audioCueService: IAudioCueService, - @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, - @IAccessibilityService private readonly _accessibilityService: IAccessibilityService - ) { - super(); - this._editor = editor; - this._options = objects.mixin(options, defaultOptions, false); - - this.disposed = false; - - this.nextIdx = -1; - this.ranges = []; - this.ignoreSelectionChange = false; - this.revealFirst = Boolean(this._options.alwaysRevealFirst); - - this._register(this._editor.onDidUpdateDiff(() => this._onDiffUpdated())); - - if (this._options.followsCaret) { - this._register(this._editor.getModifiedEditor().onDidChangeCursorPosition((e: ICursorPositionChangedEvent) => { - if (this.ignoreSelectionChange) { - return; - } - this._updateAccessibilityState(e.position.lineNumber); - this.nextIdx = -1; - })); - } - - // init things - this._init(); - } - - private _init(): void { - const changes = this._editor.getLineChanges(); - if (!changes) { - return; - } - } - - private _onDiffUpdated(): void { - this._init(); - - this._compute(this._editor.getLineChanges()); - if (this.revealFirst) { - // Only reveal first on first non-null changes - if (this._editor.getLineChanges() !== null) { - this.revealFirst = false; - this.nextIdx = -1; - this.next(ScrollType.Immediate); - } - } - } - - private _compute(lineChanges: ILineChange[] | null): void { - - // new ranges - this.ranges = []; - - if (lineChanges) { - // create ranges from changes - lineChanges.forEach((lineChange) => { - - if (!this._options.ignoreCharChanges && lineChange.charChanges) { - - lineChange.charChanges.forEach((charChange) => { - this.ranges.push({ - rhs: true, - range: new Range( - charChange.modifiedStartLineNumber, - charChange.modifiedStartColumn, - charChange.modifiedEndLineNumber, - charChange.modifiedEndColumn) - }); - }); - - } else { - if (lineChange.modifiedEndLineNumber === 0) { - // a deletion - this.ranges.push({ - rhs: true, - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedStartLineNumber + 1, 1) - }); - } else { - // an insertion or modification - this.ranges.push({ - rhs: true, - range: new Range(lineChange.modifiedStartLineNumber, 1, lineChange.modifiedEndLineNumber + 1, 1) - }); - } - } - }); - } - - // sort - this.ranges.sort((left, right) => Range.compareRangesUsingStarts(left.range, right.range)); - this._onDidUpdate.fire(this); - } - - private _initIdx(fwd: boolean): void { - let found = false; - const position = this._editor.getPosition(); - if (!position) { - this.nextIdx = 0; - return; - } - for (let i = 0, len = this.ranges.length; i < len && !found; i++) { - const range = this.ranges[i].range; - if (position.isBeforeOrEqual(range.getStartPosition())) { - this.nextIdx = i + (fwd ? 0 : -1); - found = true; - } - } - if (!found) { - // after the last change - this.nextIdx = fwd ? 0 : this.ranges.length - 1; - } - if (this.nextIdx < 0) { - this.nextIdx = this.ranges.length - 1; - } - } - - private _move(fwd: boolean, scrollType: ScrollType): void { - assert.ok(!this.disposed, 'Illegal State - diff navigator has been disposed'); - - if (!this.canNavigate()) { - return; - } - - if (this.nextIdx === -1) { - this._initIdx(fwd); - - } else if (fwd) { - this.nextIdx += 1; - if (this.nextIdx >= this.ranges.length) { - this.nextIdx = 0; - } - } else { - this.nextIdx -= 1; - if (this.nextIdx < 0) { - this.nextIdx = this.ranges.length - 1; - } - } - - const info = this.ranges[this.nextIdx]; - this.ignoreSelectionChange = true; - try { - const pos = info.range.getStartPosition(); - this._editor.setPosition(pos); - this._editor.revealRangeInCenter(info.range, scrollType); - this._updateAccessibilityState(pos.lineNumber, true); - } finally { - this.ignoreSelectionChange = false; - } - } - - _updateAccessibilityState(lineNumber: number, jumpToChange?: boolean): void { - const modifiedEditor = this._editor.getModel()?.modified; - if (!modifiedEditor) { - return; - } - const insertedOrModified = modifiedEditor.getLineDecorations(lineNumber).find(l => l.options.className === 'line-insert'); - if (insertedOrModified) { - this._audioCueService.playAudioCue(AudioCue.diffLineModified, { allowManyInParallel: true }); - } else if (jumpToChange) { - // The modified editor does not include deleted lines, but when - // we are moved to the area where lines were deleted, play this cue - this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, { allowManyInParallel: true }); - } else { - return; - } - - const codeEditor = this._codeEditorService.getActiveCodeEditor(); - if (jumpToChange && codeEditor && insertedOrModified && this._accessibilityService.isScreenReaderOptimized()) { - codeEditor.setSelection({ startLineNumber: lineNumber, startColumn: 0, endLineNumber: lineNumber, endColumn: Number.MAX_VALUE }); - codeEditor.writeScreenReaderContent('diff-navigation'); - } - } - - canNavigate(): boolean { - return this.ranges && this.ranges.length > 0; - } - - next(scrollType: ScrollType = ScrollType.Smooth): void { - if (!this.canNavigateNext()) { - return; - } - this._move(true, scrollType); - } - - previous(scrollType: ScrollType = ScrollType.Smooth): void { - if (!this.canNavigatePrevious()) { - return; - } - this._move(false, scrollType); - } - - canNavigateNext(): boolean { - return this.canNavigateLoop() || this.nextIdx < this.ranges.length - 1; - } - - canNavigatePrevious(): boolean { - return this.canNavigateLoop() || this.nextIdx !== 0; - } - - canNavigateLoop(): boolean { - return Boolean(this._options.findResultLoop); - } - - override dispose(): void { - super.dispose(); - this.ranges = []; - this.disposed = true; - } -} diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts deleted file mode 100644 index 268d41d2f3b..00000000000 --- a/src/vs/editor/browser/widget/diffReview.ts +++ /dev/null @@ -1,826 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as dom from 'vs/base/browser/dom'; -import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; -import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { Action } from 'vs/base/common/actions'; -import { Codicon } from 'vs/base/common/codicons'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { Constants } from 'vs/base/common/uint'; -import 'vs/css!./media/diffReview'; -import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; -import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { Position } from 'vs/editor/common/core/position'; -import { ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; -import { ScrollType } from 'vs/editor/common/editorCommon'; -import { ILanguageIdCodec } from 'vs/editor/common/languages'; -import { ILanguageService } from 'vs/editor/common/languages/language'; -import { ITextModel, TextModelResolvedOptions } from 'vs/editor/common/model'; -import { LineTokens } from 'vs/editor/common/tokens/lineTokens'; -import { RenderLineInput, renderViewLine2 as renderViewLine } from 'vs/editor/common/viewLayout/viewLineRenderer'; -import { ViewLineRenderingData } from 'vs/editor/common/viewModel'; -import * as nls from 'vs/nls'; -import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; - -const DIFF_LINES_PADDING = 3; - -const enum DiffEntryType { - Equal = 0, - Insert = 1, - Delete = 2 -} - -class DiffEntry { - readonly originalLineStart: number; - readonly originalLineEnd: number; - readonly modifiedLineStart: number; - readonly modifiedLineEnd: number; - - constructor(originalLineStart: number, originalLineEnd: number, modifiedLineStart: number, modifiedLineEnd: number) { - this.originalLineStart = originalLineStart; - this.originalLineEnd = originalLineEnd; - this.modifiedLineStart = modifiedLineStart; - this.modifiedLineEnd = modifiedLineEnd; - } - - public getType(): DiffEntryType { - if (this.originalLineStart === 0) { - return DiffEntryType.Insert; - } - if (this.modifiedLineStart === 0) { - return DiffEntryType.Delete; - } - return DiffEntryType.Equal; - } -} - -const enum DiffEditorLineClasses { - Insert = 'line-insert', - Delete = 'line-delete' -} - -class Diff { - readonly entries: DiffEntry[]; - - constructor(entries: DiffEntry[]) { - this.entries = entries; - } -} - -const diffReviewInsertIcon = registerIcon('diff-review-insert', Codicon.add, nls.localize('diffReviewInsertIcon', 'Icon for \'Insert\' in diff review.')); -const diffReviewRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, nls.localize('diffReviewRemoveIcon', 'Icon for \'Remove\' in diff review.')); -const diffReviewCloseIcon = registerIcon('diff-review-close', Codicon.close, nls.localize('diffReviewCloseIcon', 'Icon for \'Close\' in diff review.')); - -export class DiffReview extends Disposable { - - public static _ttPolicy = createTrustedTypesPolicy('diffReview', { createHTML: value => value }); - - private readonly _diffEditor: DiffEditorWidget; - private _isVisible: boolean; - public readonly shadow: FastDomNode; - private readonly _actionBar: ActionBar; - public readonly actionBarContainer: FastDomNode; - public readonly domNode: FastDomNode; - private readonly _content: FastDomNode; - private readonly scrollbar: DomScrollableElement; - private _diffs: Diff[]; - private _currentDiff: Diff | null; - - constructor( - diffEditor: DiffEditorWidget, - @ILanguageService private readonly _languageService: ILanguageService, - @IAudioCueService private readonly _audioCueService: IAudioCueService, - @IConfigurationService private readonly _configurationService: IConfigurationService - ) { - super(); - this._diffEditor = diffEditor; - this._isVisible = false; - - this.shadow = createFastDomNode(document.createElement('div')); - this.shadow.setClassName('diff-review-shadow'); - - this.actionBarContainer = createFastDomNode(document.createElement('div')); - this.actionBarContainer.setClassName('diff-review-actions'); - this._actionBar = this._register(new ActionBar( - this.actionBarContainer.domNode - )); - - this._actionBar.push(new Action('diffreview.close', nls.localize('label.close', "Close"), 'close-diff-review ' + ThemeIcon.asClassName(diffReviewCloseIcon), true, async () => this.hide()), { label: false, icon: true }); - - this.domNode = createFastDomNode(document.createElement('div')); - this.domNode.setClassName('diff-review monaco-editor-background'); - - this._content = createFastDomNode(document.createElement('div')); - this._content.setClassName('diff-review-content'); - this._content.setAttribute('role', 'code'); - this.scrollbar = this._register(new DomScrollableElement(this._content.domNode, {})); - this.domNode.domNode.appendChild(this.scrollbar.getDomNode()); - - this._register(diffEditor.onDidUpdateDiff(() => { - if (!this._isVisible) { - return; - } - this._diffs = this._compute(); - this._render(); - })); - this._register(diffEditor.getModifiedEditor().onDidChangeCursorPosition(() => { - if (!this._isVisible) { - return; - } - this._render(); - })); - this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'click', (e) => { - e.preventDefault(); - - const row = dom.findParentWithClass(e.target, 'diff-review-row'); - if (row) { - this._goToRow(row); - } - })); - this._register(dom.addStandardDisposableListener(this.domNode.domNode, 'keydown', (e) => { - if ( - e.equals(KeyCode.DownArrow) - || e.equals(KeyMod.CtrlCmd | KeyCode.DownArrow) - || e.equals(KeyMod.Alt | KeyCode.DownArrow) - ) { - e.preventDefault(); - this._goToRow(this._getNextRow(), 'next'); - } - - if ( - e.equals(KeyCode.UpArrow) - || e.equals(KeyMod.CtrlCmd | KeyCode.UpArrow) - || e.equals(KeyMod.Alt | KeyCode.UpArrow) - ) { - e.preventDefault(); - this._goToRow(this._getPrevRow(), 'previous'); - } - - if ( - e.equals(KeyCode.Escape) - || e.equals(KeyMod.CtrlCmd | KeyCode.Escape) - || e.equals(KeyMod.Alt | KeyCode.Escape) - || e.equals(KeyMod.Shift | KeyCode.Escape) - || e.equals(KeyCode.Space) - || e.equals(KeyCode.Enter) - ) { - e.preventDefault(); - this.accept(); - } - })); - this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('accessibility.verbosity.diffEditor')) { - this._diffEditor.updateOptions({ accessibilityVerbose: this._configurationService.getValue('accessibility.verbosity.diffEditor') }); - } - })); - this._diffs = []; - this._currentDiff = null; - } - - public prev(): void { - let index = 0; - - if (!this._isVisible) { - this._diffs = this._compute(); - } - - if (this._isVisible) { - let currentIndex = -1; - for (let i = 0, len = this._diffs.length; i < len; i++) { - if (this._diffs[i] === this._currentDiff) { - currentIndex = i; - break; - } - } - index = (this._diffs.length + currentIndex - 1); - } else { - index = this._findDiffIndex(this._diffEditor.getPosition()!); - } - - if (this._diffs.length === 0) { - // Nothing to do - return; - } - - index = index % this._diffs.length; - const entries = this._diffs[index].entries; - this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); - this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); - this._isVisible = true; - this._diffEditor.doLayout(); - this._render(); - this._goToRow(this._getPrevRow(), 'previous'); - } - - public next(): void { - let index = 0; - - if (!this._isVisible) { - this._diffs = this._compute(); - } - - if (this._isVisible) { - let currentIndex = -1; - for (let i = 0, len = this._diffs.length; i < len; i++) { - if (this._diffs[i] === this._currentDiff) { - currentIndex = i; - break; - } - } - index = (currentIndex + 1); - } else { - index = this._findDiffIndex(this._diffEditor.getPosition()!); - } - - if (this._diffs.length === 0) { - // Nothing to do - return; - } - - index = index % this._diffs.length; - const entries = this._diffs[index].entries; - this._diffEditor.setPosition(new Position(entries[0].modifiedLineStart, 1)); - this._diffEditor.setSelection({ startColumn: 1, startLineNumber: entries[0].modifiedLineStart, endColumn: Constants.MAX_SAFE_SMALL_INTEGER, endLineNumber: entries[entries.length - 1].modifiedLineEnd }); - this._isVisible = true; - this._diffEditor.doLayout(); - this._render(); - this._goToRow(this._getNextRow(), 'next'); - } - - private accept(): void { - let jumpToLineNumber = -1; - const current = this._getCurrentFocusedRow(); - if (current) { - const lineNumber = parseInt(current.getAttribute('data-line')!, 10); - if (!isNaN(lineNumber)) { - jumpToLineNumber = lineNumber; - } - } - this.hide(); - - if (jumpToLineNumber !== -1) { - this._diffEditor.setPosition(new Position(jumpToLineNumber, 1)); - this._diffEditor.revealPosition(new Position(jumpToLineNumber, 1), ScrollType.Immediate); - } - } - - private hide(): void { - this._isVisible = false; - this._diffEditor.updateOptions({ readOnly: false }); - this._diffEditor.focus(); - this._diffEditor.doLayout(); - this._render(); - } - - private _getPrevRow(): HTMLElement { - const current = this._getCurrentFocusedRow(); - if (!current) { - return this._getFirstRow(); - } - if (current.previousElementSibling) { - return current.previousElementSibling; - } - return current; - } - - private _getNextRow(): HTMLElement { - const current = this._getCurrentFocusedRow(); - if (!current) { - return this._getFirstRow(); - } - if (current.nextElementSibling) { - return current.nextElementSibling; - } - return current; - } - - private _getFirstRow(): HTMLElement { - return this.domNode.domNode.querySelector('.diff-review-row'); - } - - private _getCurrentFocusedRow(): HTMLElement | null { - const result = document.activeElement; - if (result && /diff-review-row/.test(result.className)) { - return result; - } - return null; - } - - private _goToRow(row: HTMLElement, type?: 'next' | 'previous'): void { - const current = this._getCurrentFocusedRow(); - row.tabIndex = 0; - row.focus(); - if (current && current !== row) { - current.tabIndex = -1; - } - const element = !type ? current : type === 'next' ? current?.nextElementSibling : current?.previousElementSibling; - if (element?.classList.contains(DiffEditorLineClasses.Insert)) { - this._audioCueService.playAudioCue(AudioCue.diffLineInserted, { allowManyInParallel: true }); - } else if (element?.classList.contains(DiffEditorLineClasses.Delete)) { - this._audioCueService.playAudioCue(AudioCue.diffLineDeleted, { allowManyInParallel: true }); - } - this.scrollbar.scanDomNode(); - } - - public isVisible(): boolean { - return this._isVisible; - } - - private _width: number = 0; - - public layout(top: number, width: number, height: number): void { - this._width = width; - this.shadow.setTop(top - 6); - this.shadow.setWidth(width); - this.shadow.setHeight(this._isVisible ? 6 : 0); - this.domNode.setTop(top); - this.domNode.setWidth(width); - this.domNode.setHeight(height); - this._content.setHeight(height); - this._content.setWidth(width); - - if (this._isVisible) { - this.actionBarContainer.setAttribute('aria-hidden', 'false'); - this.actionBarContainer.setDisplay('block'); - } else { - this.actionBarContainer.setAttribute('aria-hidden', 'true'); - this.actionBarContainer.setDisplay('none'); - } - } - - private _compute(): Diff[] { - const lineChanges = this._diffEditor.getLineChanges(); - if (!lineChanges || lineChanges.length === 0) { - return []; - } - const originalModel = this._diffEditor.getOriginalEditor().getModel(); - const modifiedModel = this._diffEditor.getModifiedEditor().getModel(); - - if (!originalModel || !modifiedModel) { - return []; - } - - return DiffReview._mergeAdjacent(lineChanges, originalModel.getLineCount(), modifiedModel.getLineCount()); - } - - private static _mergeAdjacent(lineChanges: ILineChange[], originalLineCount: number, modifiedLineCount: number): Diff[] { - if (!lineChanges || lineChanges.length === 0) { - return []; - } - - const diffs: Diff[] = []; - let diffsLength = 0; - - for (let i = 0, len = lineChanges.length; i < len; i++) { - const lineChange = lineChanges[i]; - - const originalStart = lineChange.originalStartLineNumber; - const originalEnd = lineChange.originalEndLineNumber; - const modifiedStart = lineChange.modifiedStartLineNumber; - const modifiedEnd = lineChange.modifiedEndLineNumber; - - const r: DiffEntry[] = []; - let rLength = 0; - - // Emit before anchors - { - const originalEqualAbove = (originalEnd === 0 ? originalStart : originalStart - 1); - const modifiedEqualAbove = (modifiedEnd === 0 ? modifiedStart : modifiedStart - 1); - - // Make sure we don't step into the previous diff - let minOriginal = 1; - let minModified = 1; - if (i > 0) { - const prevLineChange = lineChanges[i - 1]; - - if (prevLineChange.originalEndLineNumber === 0) { - minOriginal = prevLineChange.originalStartLineNumber + 1; - } else { - minOriginal = prevLineChange.originalEndLineNumber + 1; - } - - if (prevLineChange.modifiedEndLineNumber === 0) { - minModified = prevLineChange.modifiedStartLineNumber + 1; - } else { - minModified = prevLineChange.modifiedEndLineNumber + 1; - } - } - - let fromOriginal = originalEqualAbove - DIFF_LINES_PADDING + 1; - let fromModified = modifiedEqualAbove - DIFF_LINES_PADDING + 1; - if (fromOriginal < minOriginal) { - const delta = minOriginal - fromOriginal; - fromOriginal = fromOriginal + delta; - fromModified = fromModified + delta; - } - if (fromModified < minModified) { - const delta = minModified - fromModified; - fromOriginal = fromOriginal + delta; - fromModified = fromModified + delta; - } - - r[rLength++] = new DiffEntry( - fromOriginal, originalEqualAbove, - fromModified, modifiedEqualAbove - ); - } - - // Emit deleted lines - { - if (originalEnd !== 0) { - r[rLength++] = new DiffEntry(originalStart, originalEnd, 0, 0); - } - } - - // Emit inserted lines - { - if (modifiedEnd !== 0) { - r[rLength++] = new DiffEntry(0, 0, modifiedStart, modifiedEnd); - } - } - - // Emit after anchors - { - const originalEqualBelow = (originalEnd === 0 ? originalStart + 1 : originalEnd + 1); - const modifiedEqualBelow = (modifiedEnd === 0 ? modifiedStart + 1 : modifiedEnd + 1); - - // Make sure we don't step into the next diff - let maxOriginal = originalLineCount; - let maxModified = modifiedLineCount; - if (i + 1 < len) { - const nextLineChange = lineChanges[i + 1]; - - if (nextLineChange.originalEndLineNumber === 0) { - maxOriginal = nextLineChange.originalStartLineNumber; - } else { - maxOriginal = nextLineChange.originalStartLineNumber - 1; - } - - if (nextLineChange.modifiedEndLineNumber === 0) { - maxModified = nextLineChange.modifiedStartLineNumber; - } else { - maxModified = nextLineChange.modifiedStartLineNumber - 1; - } - } - - let toOriginal = originalEqualBelow + DIFF_LINES_PADDING - 1; - let toModified = modifiedEqualBelow + DIFF_LINES_PADDING - 1; - - if (toOriginal > maxOriginal) { - const delta = maxOriginal - toOriginal; - toOriginal = toOriginal + delta; - toModified = toModified + delta; - } - if (toModified > maxModified) { - const delta = maxModified - toModified; - toOriginal = toOriginal + delta; - toModified = toModified + delta; - } - - r[rLength++] = new DiffEntry( - originalEqualBelow, toOriginal, - modifiedEqualBelow, toModified, - ); - } - - diffs[diffsLength++] = new Diff(r); - } - - // Merge adjacent diffs - let curr: DiffEntry[] = diffs[0].entries; - const r: Diff[] = []; - let rLength = 0; - for (let i = 1, len = diffs.length; i < len; i++) { - const thisDiff = diffs[i].entries; - - const currLast = curr[curr.length - 1]; - const thisFirst = thisDiff[0]; - - if ( - currLast.getType() === DiffEntryType.Equal - && thisFirst.getType() === DiffEntryType.Equal - && thisFirst.originalLineStart <= currLast.originalLineEnd - ) { - // We are dealing with equal lines that overlap - - curr[curr.length - 1] = new DiffEntry( - currLast.originalLineStart, thisFirst.originalLineEnd, - currLast.modifiedLineStart, thisFirst.modifiedLineEnd - ); - curr = curr.concat(thisDiff.slice(1)); - continue; - } - - r[rLength++] = new Diff(curr); - curr = thisDiff; - } - r[rLength++] = new Diff(curr); - return r; - } - - private _findDiffIndex(pos: Position): number { - const lineNumber = pos.lineNumber; - for (let i = 0, len = this._diffs.length; i < len; i++) { - const diff = this._diffs[i].entries; - const lastModifiedLine = diff[diff.length - 1].modifiedLineEnd; - if (lineNumber <= lastModifiedLine) { - return i; - } - } - return 0; - } - - private _render(): void { - - const originalOptions = this._diffEditor.getOriginalEditor().getOptions(); - const modifiedOptions = this._diffEditor.getModifiedEditor().getOptions(); - - const originalModel = this._diffEditor.getOriginalEditor().getModel(); - const modifiedModel = this._diffEditor.getModifiedEditor().getModel(); - - const originalModelOpts = originalModel!.getOptions(); - const modifiedModelOpts = modifiedModel!.getOptions(); - - if (!this._isVisible || !originalModel || !modifiedModel) { - dom.clearNode(this._content.domNode); - this._currentDiff = null; - this.scrollbar.scanDomNode(); - return; - } - - this._diffEditor.updateOptions({ readOnly: true }); - const diffIndex = this._findDiffIndex(this._diffEditor.getPosition()!); - - if (this._diffs[diffIndex] === this._currentDiff) { - return; - } - this._currentDiff = this._diffs[diffIndex]; - - const diffs = this._diffs[diffIndex].entries; - const container = document.createElement('div'); - container.className = 'diff-review-table'; - container.setAttribute('role', 'list'); - container.setAttribute('aria-label', 'Difference review. Use "Stage | Unstage | Revert Selected Ranges" commands'); - applyFontInfo(container, modifiedOptions.get(EditorOption.fontInfo)); - - let minOriginalLine = 0; - let maxOriginalLine = 0; - let minModifiedLine = 0; - let maxModifiedLine = 0; - for (let i = 0, len = diffs.length; i < len; i++) { - const diffEntry = diffs[i]; - const originalLineStart = diffEntry.originalLineStart; - const originalLineEnd = diffEntry.originalLineEnd; - const modifiedLineStart = diffEntry.modifiedLineStart; - const modifiedLineEnd = diffEntry.modifiedLineEnd; - - if (originalLineStart !== 0 && ((minOriginalLine === 0 || originalLineStart < minOriginalLine))) { - minOriginalLine = originalLineStart; - } - if (originalLineEnd !== 0 && ((maxOriginalLine === 0 || originalLineEnd > maxOriginalLine))) { - maxOriginalLine = originalLineEnd; - } - if (modifiedLineStart !== 0 && ((minModifiedLine === 0 || modifiedLineStart < minModifiedLine))) { - minModifiedLine = modifiedLineStart; - } - if (modifiedLineEnd !== 0 && ((maxModifiedLine === 0 || modifiedLineEnd > maxModifiedLine))) { - maxModifiedLine = modifiedLineEnd; - } - } - - const header = document.createElement('div'); - header.className = 'diff-review-row'; - - const cell = document.createElement('div'); - cell.className = 'diff-review-cell diff-review-summary'; - const originalChangedLinesCnt = maxOriginalLine - minOriginalLine + 1; - const modifiedChangedLinesCnt = maxModifiedLine - minModifiedLine + 1; - cell.appendChild(document.createTextNode(`${diffIndex + 1}/${this._diffs.length}: @@ -${minOriginalLine},${originalChangedLinesCnt} +${minModifiedLine},${modifiedChangedLinesCnt} @@`)); - header.setAttribute('data-line', String(minModifiedLine)); - - const getAriaLines = (lines: number) => { - if (lines === 0) { - return nls.localize('no_lines_changed', "no lines changed"); - } else if (lines === 1) { - return nls.localize('one_line_changed', "1 line changed"); - } else { - return nls.localize('more_lines_changed', "{0} lines changed", lines); - } - }; - - const originalChangedLinesCntAria = getAriaLines(originalChangedLinesCnt); - const modifiedChangedLinesCntAria = getAriaLines(modifiedChangedLinesCnt); - header.setAttribute('aria-label', nls.localize({ - key: 'header', - comment: [ - 'This is the ARIA label for a git diff header.', - 'A git diff header looks like this: @@ -154,12 +159,39 @@.', - 'That encodes that at original line 154 (which is now line 159), 12 lines were removed/changed with 39 lines.', - 'Variables 0 and 1 refer to the diff index out of total number of diffs.', - 'Variables 2 and 4 will be numbers (a line number).', - 'Variables 3 and 5 will be "no lines changed", "1 line changed" or "X lines changed", localized separately.' - ] - }, "Difference {0} of {1}: original line {2}, {3}, modified line {4}, {5}", (diffIndex + 1), this._diffs.length, minOriginalLine, originalChangedLinesCntAria, minModifiedLine, modifiedChangedLinesCntAria)); - header.appendChild(cell); - - // @@ -504,7 +517,7 @@ - header.setAttribute('role', 'listitem'); - container.appendChild(header); - - const lineHeight = modifiedOptions.get(EditorOption.lineHeight); - let modLine = minModifiedLine; - for (let i = 0, len = diffs.length; i < len; i++) { - const diffEntry = diffs[i]; - DiffReview._renderSection(container, diffEntry, modLine, lineHeight, this._width, originalOptions, originalModel, originalModelOpts, modifiedOptions, modifiedModel, modifiedModelOpts, this._languageService.languageIdCodec); - if (diffEntry.modifiedLineStart !== 0) { - modLine = diffEntry.modifiedLineEnd; - } - } - - dom.clearNode(this._content.domNode); - this._content.domNode.appendChild(container); - this.scrollbar.scanDomNode(); - } - - private static _renderSection( - dest: HTMLElement, diffEntry: DiffEntry, modLine: number, lineHeight: number, width: number, - originalOptions: IComputedEditorOptions, originalModel: ITextModel, originalModelOpts: TextModelResolvedOptions, - modifiedOptions: IComputedEditorOptions, modifiedModel: ITextModel, modifiedModelOpts: TextModelResolvedOptions, - languageIdCodec: ILanguageIdCodec - ): void { - - const type = diffEntry.getType(); - - let rowClassName: string = 'diff-review-row'; - let lineNumbersExtraClassName: string = ''; - const spacerClassName: string = 'diff-review-spacer'; - let spacerIcon: ThemeIcon | null = null; - switch (type) { - case DiffEntryType.Insert: - rowClassName = 'diff-review-row line-insert'; - lineNumbersExtraClassName = ' char-insert'; - spacerIcon = diffReviewInsertIcon; - break; - case DiffEntryType.Delete: - rowClassName = 'diff-review-row line-delete'; - lineNumbersExtraClassName = ' char-delete'; - spacerIcon = diffReviewRemoveIcon; - break; - } - - const originalLineStart = diffEntry.originalLineStart; - const originalLineEnd = diffEntry.originalLineEnd; - const modifiedLineStart = diffEntry.modifiedLineStart; - const modifiedLineEnd = diffEntry.modifiedLineEnd; - - const cnt = Math.max( - modifiedLineEnd - modifiedLineStart, - originalLineEnd - originalLineStart - ); - - const originalLayoutInfo = originalOptions.get(EditorOption.layoutInfo); - const originalLineNumbersWidth = originalLayoutInfo.glyphMarginWidth + originalLayoutInfo.lineNumbersWidth; - - const modifiedLayoutInfo = modifiedOptions.get(EditorOption.layoutInfo); - const modifiedLineNumbersWidth = 10 + modifiedLayoutInfo.glyphMarginWidth + modifiedLayoutInfo.lineNumbersWidth; - - for (let i = 0; i <= cnt; i++) { - const originalLine = (originalLineStart === 0 ? 0 : originalLineStart + i); - const modifiedLine = (modifiedLineStart === 0 ? 0 : modifiedLineStart + i); - - const row = document.createElement('div'); - row.style.minWidth = width + 'px'; - row.className = rowClassName; - row.setAttribute('role', 'listitem'); - if (modifiedLine !== 0) { - modLine = modifiedLine; - } - row.setAttribute('data-line', String(modLine)); - - const cell = document.createElement('div'); - cell.className = 'diff-review-cell'; - cell.style.height = `${lineHeight}px`; - row.appendChild(cell); - - const originalLineNumber = document.createElement('span'); - originalLineNumber.style.width = (originalLineNumbersWidth + 'px'); - originalLineNumber.style.minWidth = (originalLineNumbersWidth + 'px'); - originalLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; - if (originalLine !== 0) { - originalLineNumber.appendChild(document.createTextNode(String(originalLine))); - } else { - originalLineNumber.innerText = '\u00a0'; - } - cell.appendChild(originalLineNumber); - - const modifiedLineNumber = document.createElement('span'); - modifiedLineNumber.style.width = (modifiedLineNumbersWidth + 'px'); - modifiedLineNumber.style.minWidth = (modifiedLineNumbersWidth + 'px'); - modifiedLineNumber.style.paddingRight = '10px'; - modifiedLineNumber.className = 'diff-review-line-number' + lineNumbersExtraClassName; - if (modifiedLine !== 0) { - modifiedLineNumber.appendChild(document.createTextNode(String(modifiedLine))); - } else { - modifiedLineNumber.innerText = '\u00a0'; - } - cell.appendChild(modifiedLineNumber); - - const spacer = document.createElement('span'); - spacer.className = spacerClassName; - - if (spacerIcon) { - const spacerCodicon = document.createElement('span'); - spacerCodicon.className = ThemeIcon.asClassName(spacerIcon); - spacerCodicon.innerText = '\u00a0\u00a0'; - spacer.appendChild(spacerCodicon); - } else { - spacer.innerText = '\u00a0\u00a0'; - } - cell.appendChild(spacer); - - let lineContent: string; - if (modifiedLine !== 0) { - let html: string | TrustedHTML = this._renderLine(modifiedModel, modifiedOptions, modifiedModelOpts.tabSize, modifiedLine, languageIdCodec); - if (DiffReview._ttPolicy) { - html = DiffReview._ttPolicy.createHTML(html as string); - } - cell.insertAdjacentHTML('beforeend', html as string); - lineContent = modifiedModel.getLineContent(modifiedLine); - } else { - let html: string | TrustedHTML = this._renderLine(originalModel, originalOptions, originalModelOpts.tabSize, originalLine, languageIdCodec); - if (DiffReview._ttPolicy) { - html = DiffReview._ttPolicy.createHTML(html as string); - } - cell.insertAdjacentHTML('beforeend', html as string); - lineContent = originalModel.getLineContent(originalLine); - } - - if (lineContent.length === 0) { - lineContent = nls.localize('blankLine', "blank"); - } - - let ariaLabel: string = ''; - switch (type) { - case DiffEntryType.Equal: - if (originalLine === modifiedLine) { - ariaLabel = nls.localize({ key: 'unchangedLine', comment: ['The placeholders are contents of the line and should not be translated.'] }, "{0} unchanged line {1}", lineContent, originalLine); - } else { - ariaLabel = nls.localize('equalLine', "{0} original line {1} modified line {2}", lineContent, originalLine, modifiedLine); - } - break; - case DiffEntryType.Insert: - ariaLabel = nls.localize('insertLine', "+ {0} modified line {1}", lineContent, modifiedLine); - break; - case DiffEntryType.Delete: - ariaLabel = nls.localize('deleteLine', "- {0} original line {1}", lineContent, originalLine); - break; - } - row.setAttribute('aria-label', ariaLabel); - - dest.appendChild(row); - } - } - - private static _renderLine(model: ITextModel, options: IComputedEditorOptions, tabSize: number, lineNumber: number, languageIdCodec: ILanguageIdCodec): string { - const lineContent = model.getLineContent(lineNumber); - const fontInfo = options.get(EditorOption.fontInfo); - const lineTokens = LineTokens.createEmpty(lineContent, languageIdCodec); - const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, model.mightContainNonBasicASCII()); - const containsRTL = ViewLineRenderingData.containsRTL(lineContent, isBasicASCII, model.mightContainRTL()); - const r = renderViewLine(new RenderLineInput( - (fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations)), - fontInfo.canUseHalfwidthRightwardsArrow, - lineContent, - false, - isBasicASCII, - containsRTL, - 0, - lineTokens, - [], - tabSize, - 0, - fontInfo.spaceWidth, - fontInfo.middotWidth, - fontInfo.wsmiddotWidth, - options.get(EditorOption.stopRenderingLineAfter), - options.get(EditorOption.renderWhitespace), - options.get(EditorOption.renderControlCharacters), - options.get(EditorOption.fontLigatures) !== EditorFontLigatures.OFF, - null - )); - - return r.html; - } -} - -// theming diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index d9785bfc8c1..78d8dffd609 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -7,21 +7,18 @@ import * as objects from 'vs/base/common/objects'; import { ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget, IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget'; +import { DiffEditorWidget2, IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; import { ConfigurationChangedEvent, IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IEditorProgressService } from 'vs/platform/progress/common/progress'; -import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; -import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; export class EmbeddedCodeEditorWidget extends CodeEditorWidget { @@ -69,54 +66,6 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { } } -/** - * @deprecated Use EmbeddedDiffEditorWidget2 instead. - */ -export class EmbeddedDiffEditorWidget extends DiffEditorWidget { - - private readonly _parentEditor: ICodeEditor; - private readonly _overwriteOptions: IDiffEditorOptions; - - constructor( - domElement: HTMLElement, - options: Readonly, - codeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, - parentEditor: ICodeEditor, - @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService, - @ICodeEditorService codeEditorService: ICodeEditorService, - @IThemeService themeService: IThemeService, - @INotificationService notificationService: INotificationService, - @IContextMenuService contextMenuService: IContextMenuService, - @IClipboardService clipboardService: IClipboardService, - @IEditorProgressService editorProgressService: IEditorProgressService, - ) { - super(domElement, parentEditor.getRawOptions(), codeEditorWidgetOptions, clipboardService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, editorProgressService); - - this._parentEditor = parentEditor; - this._overwriteOptions = options; - - // Overwrite parent's options - super.updateOptions(this._overwriteOptions); - - this._register(parentEditor.onDidChangeConfiguration(e => this._onParentConfigurationChanged(e))); - } - - getParentEditor(): ICodeEditor { - return this._parentEditor; - } - - private _onParentConfigurationChanged(e: ConfigurationChangedEvent): void { - super.updateOptions(this._parentEditor.getRawOptions()); - super.updateOptions(this._overwriteOptions); - } - - override updateOptions(newOptions: IEditorOptions): void { - objects.mixin(this._overwriteOptions, newOptions, true); - super.updateOptions(this._overwriteOptions); - } -} - /** * TODO: Rename to EmbeddedDiffEditorWidget once EmbeddedDiffEditorWidget is removed. */ diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 684ec85c33b..5f18e0ce03f 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -217,36 +217,30 @@ const editorConfiguration: IConfigurationNode = { 'diffEditor.hideUnchangedRegions.enabled': { type: 'boolean', default: diffEditorDefaultOptions.hideUnchangedRegions.enabled, - markdownDescription: nls.localize('hideUnchangedRegions.enabled', "Controls whether the diff editor shows unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + markdownDescription: nls.localize('hideUnchangedRegions.enabled', "Controls whether the diff editor shows unchanged regions."), }, 'diffEditor.hideUnchangedRegions.revealLineCount': { type: 'integer', default: diffEditorDefaultOptions.hideUnchangedRegions.revealLineCount, - markdownDescription: nls.localize('hideUnchangedRegions.revealLineCount', "Controls how many lines are used for unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + markdownDescription: nls.localize('hideUnchangedRegions.revealLineCount', "Controls how many lines are used for unchanged regions."), minimum: 1, }, 'diffEditor.hideUnchangedRegions.minimumLineCount': { type: 'integer', default: diffEditorDefaultOptions.hideUnchangedRegions.minimumLineCount, - markdownDescription: nls.localize('hideUnchangedRegions.minimumLineCount', "Controls how many lines are used as a minimum for unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + markdownDescription: nls.localize('hideUnchangedRegions.minimumLineCount', "Controls how many lines are used as a minimum for unchanged regions."), minimum: 1, }, 'diffEditor.hideUnchangedRegions.contextLineCount': { type: 'integer', default: diffEditorDefaultOptions.hideUnchangedRegions.contextLineCount, - markdownDescription: nls.localize('hideUnchangedRegions.contextLineCount', "Controls how many lines are used as context when comparing unchanged regions. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`'), + markdownDescription: nls.localize('hideUnchangedRegions.contextLineCount', "Controls how many lines are used as context when comparing unchanged regions."), minimum: 1, }, 'diffEditor.experimental.showMoves': { type: 'boolean', default: diffEditorDefaultOptions.experimental.showMoves, - markdownDescription: nls.localize('showMoves', "Controls whether the diff editor should show detected code moves. Only works when {0} is set.", '`#diffEditor.experimental.useVersion2#`') - }, - 'diffEditor.experimental.useVersion2': { - type: 'boolean', - default: true, - description: nls.localize('useVersion2', "Controls whether the diff editor uses the new or the old implementation."), - tags: ['experimental'], + markdownDescription: nls.localize('showMoves', "Controls whether the diff editor should show detected code moves.") }, 'diffEditor.experimental.showEmptyDecorations': { type: 'boolean', diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 71eedebd3b9..8cb672bb8dc 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -5,8 +5,7 @@ import 'vs/editor/browser/coreCommands'; import 'vs/editor/browser/widget/codeEditorWidget'; -import 'vs/editor/browser/widget/diffEditorWidget'; -import 'vs/editor/browser/widget/diffNavigator'; +import 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; import 'vs/editor/contrib/anchorSelect/browser/anchorSelect'; import 'vs/editor/contrib/bracketMatching/browser/bracketMatching'; import 'vs/editor/contrib/caretOperations/browser/caretOperations'; diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index c2eb25b69e9..7138127f621 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -8,7 +8,6 @@ import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/ import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { InternalEditorAction } from 'vs/editor/common/editorAction'; import { IModelChangedEvent } from 'vs/editor/common/editorCommon'; @@ -481,82 +480,6 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon } } -export class StandaloneDiffEditor extends DiffEditorWidget implements IStandaloneDiffEditor { - - private readonly _configurationService: IConfigurationService; - private readonly _standaloneThemeService: IStandaloneThemeService; - - constructor( - domElement: HTMLElement, - _options: Readonly | undefined, - @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, - @ICodeEditorService codeEditorService: ICodeEditorService, - @IStandaloneThemeService themeService: IStandaloneThemeService, - @INotificationService notificationService: INotificationService, - @IConfigurationService configurationService: IConfigurationService, - @IContextMenuService contextMenuService: IContextMenuService, - @IEditorProgressService editorProgressService: IEditorProgressService, - @IClipboardService clipboardService: IClipboardService - ) { - const options = { ..._options }; - updateConfigurationService(configurationService, options, true); - const themeDomRegistration = (themeService).registerEditorContainer(domElement); - if (typeof options.theme === 'string') { - themeService.setTheme(options.theme); - } - if (typeof options.autoDetectHighContrast !== 'undefined') { - themeService.setAutoDetectHighContrast(Boolean(options.autoDetectHighContrast)); - } - - super(domElement, options, {}, clipboardService, contextKeyService, instantiationService, codeEditorService, themeService, notificationService, contextMenuService, editorProgressService); - - this._configurationService = configurationService; - this._standaloneThemeService = themeService; - - this._register(themeDomRegistration); - } - - public override dispose(): void { - super.dispose(); - } - - public override updateOptions(newOptions: Readonly): void { - updateConfigurationService(this._configurationService, newOptions, true); - if (typeof newOptions.theme === 'string') { - this._standaloneThemeService.setTheme(newOptions.theme); - } - if (typeof newOptions.autoDetectHighContrast !== 'undefined') { - this._standaloneThemeService.setAutoDetectHighContrast(Boolean(newOptions.autoDetectHighContrast)); - } - super.updateOptions(newOptions); - } - - protected override _createInnerEditor(instantiationService: IInstantiationService, container: HTMLElement, options: Readonly): CodeEditorWidget { - return instantiationService.createInstance(StandaloneCodeEditor, container, options); - } - - public override getOriginalEditor(): IStandaloneCodeEditor { - return super.getOriginalEditor(); - } - - public override getModifiedEditor(): IStandaloneCodeEditor { - return super.getModifiedEditor(); - } - - public addCommand(keybinding: number, handler: ICommandHandler, context?: string): string | null { - return this.getModifiedEditor().addCommand(keybinding, handler, context); - } - - public createContextKey(key: string, defaultValue: T): IContextKey { - return this.getModifiedEditor().createContextKey(key, defaultValue); - } - - public addAction(descriptor: IActionDescriptor): IDisposable { - return this.getModifiedEditor().addAction(descriptor); - } -} - export class StandaloneDiffEditor2 extends DiffEditorWidget2 implements IStandaloneDiffEditor { private readonly _configurationService: IConfigurationService; diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 8f8caf549a3..92cf32cf2d5 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -12,7 +12,6 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCreateWebWorker } from 'vs/editor/browser/services/webWorker'; -import { DiffNavigator, IDiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { ApplyUpdateResult, ConfigurationChangedEvent, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; @@ -28,7 +27,7 @@ import { FindMatch, ITextModel, TextModelResolvedOptions } from 'vs/editor/commo import { IModelService } from 'vs/editor/common/services/model'; import * as standaloneEnums from 'vs/editor/common/standalone/standaloneEnums'; import { Colorizer, IColorizerElementOptions, IColorizerOptions } from 'vs/editor/standalone/browser/colorizer'; -import { IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor, StandaloneDiffEditor2, StandaloneEditor, createTextModel } from 'vs/editor/standalone/browser/standaloneCodeEditor'; +import { IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor2, StandaloneEditor, createTextModel } from 'vs/editor/standalone/browser/standaloneCodeEditor'; import { IEditorOverrideServices, StandaloneKeybindingService, StandaloneServices } from 'vs/editor/standalone/browser/standaloneServices'; import { StandaloneThemeService } from 'vs/editor/standalone/browser/standaloneThemeService'; import { IStandaloneThemeData, IStandaloneThemeService } from 'vs/editor/standalone/common/standaloneTheme'; @@ -96,21 +95,7 @@ export function getDiffEditors(): readonly IDiffEditor[] { */ export function createDiffEditor(domElement: HTMLElement, options?: IStandaloneDiffEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneDiffEditor { const instantiationService = StandaloneServices.initialize(override || {}); - if ((options?.experimental as any)?.useVersion2) { - return instantiationService.createInstance(StandaloneDiffEditor2, domElement, options); - } - return instantiationService.createInstance(StandaloneDiffEditor, domElement, options); -} - -export interface IDiffNavigatorOptions { - readonly followsCaret?: boolean; - readonly ignoreCharChanges?: boolean; - readonly alwaysRevealFirst?: boolean; -} - -export function createDiffNavigator(diffEditor: IStandaloneDiffEditor, opts?: IDiffNavigatorOptions): IDiffNavigator { - const instantiationService = StandaloneServices.initialize({}); - return instantiationService.createInstance(DiffNavigator, diffEditor, opts); + return instantiationService.createInstance(StandaloneDiffEditor2, domElement, options); } /** @@ -516,7 +501,6 @@ export function createMonacoEditorAPI(): typeof monaco.editor { onDidCreateEditor: onDidCreateEditor, onDidCreateDiffEditor: onDidCreateDiffEditor, createDiffEditor: createDiffEditor, - createDiffNavigator: createDiffNavigator, addCommand: addCommand, addEditorAction: addEditorAction, diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 168b46965be..2148581e50f 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -937,13 +937,6 @@ declare namespace monaco { declare namespace monaco.editor { - export interface IDiffNavigator { - canNavigate(): boolean; - next(): void; - previous(): void; - dispose(): void; - } - /** * Create a new editor under `domElement`. * `domElement` should be empty (not contain other dom nodes). @@ -981,14 +974,6 @@ declare namespace monaco.editor { */ export function createDiffEditor(domElement: HTMLElement, options?: IStandaloneDiffEditorConstructionOptions, override?: IEditorOverrideServices): IStandaloneDiffEditor; - export interface IDiffNavigatorOptions { - readonly followsCaret?: boolean; - readonly ignoreCharChanges?: boolean; - readonly alwaysRevealFirst?: boolean; - } - - export function createDiffNavigator(diffEditor: IStandaloneDiffEditor, opts?: IDiffNavigatorOptions): IDiffNavigator; - /** * Description of a command contribution */ diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index adb0fcddd35..58377fcf767 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -13,7 +13,6 @@ import { TEXT_DIFF_EDITOR_ID, IEditorFactoryRegistry, EditorExtensions, ITextDif import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -22,7 +21,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IThemeService } from 'vs/platform/theme/common/themeService'; import { TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { URI } from 'vs/base/common/uri'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -43,12 +41,9 @@ import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/di */ export class TextDiffEditor extends AbstractTextEditor implements ITextDiffEditorPane { static readonly ID = TEXT_DIFF_EDITOR_ID; - private static widgetCounter = 0; // Just for debugging private diffEditorControl: IDiffEditor | undefined = undefined; - private readonly diffNavigatorDisposables = this._register(new DisposableStore()); - private inputLifecycleStopWatch: StopWatch | undefined = undefined; override get scopedContextKeyService(): IContextKeyService | undefined { @@ -85,18 +80,7 @@ export class TextDiffEditor extends AbstractTextEditor imp } protected override createEditorControl(parent: HTMLElement, configuration: ICodeEditorOptions): void { - TextDiffEditor.widgetCounter++; - let useVersion2 = this.textResourceConfigurationService.getValue(undefined, 'diffEditor.experimental.useVersion2'); - if (useVersion2 === 'first') { - // This allows to have both the old and new diff editor next to each other - just for debugging - useVersion2 = TextDiffEditor.widgetCounter === 1; - } - - if (useVersion2) { - this.diffEditorControl = this._register(this.instantiationService.createInstance(DiffEditorWidget2, parent, configuration, {})); - } else { - this.diffEditorControl = this._register(this.instantiationService.createInstance(DiffEditorWidget, parent, configuration, {})); - } + this.diffEditorControl = this._register(this.instantiationService.createInstance(DiffEditorWidget2, parent, configuration, {})); } protected updateEditorControlOptions(options: ICodeEditorOptions): void { @@ -111,7 +95,6 @@ export class TextDiffEditor extends AbstractTextEditor imp // Cleanup previous things associated with the input this.inputLifecycleStopWatch = undefined; - this.diffNavigatorDisposables.clear(); // Set input and resolve await super.setInput(input, options, context, token); @@ -324,9 +307,6 @@ export class TextDiffEditor extends AbstractTextEditor imp this.logInputLifecycleTelemetry(inputLifecycleElapsed, this.getControl()?.getModel()?.modified?.getLanguageId()); } - // Dispose previous diff navigator - this.diffNavigatorDisposables.clear(); - // Clear Model this.diffEditorControl?.setModel(null); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index f9f2bd13d28..d8c0d322bf3 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -10,7 +10,7 @@ import { registerDiffEditorContribution } from 'vs/editor/browser/editorExtensio import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev } from 'vs/editor/browser/widget/diffEditor.contribution'; import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; -import { EmbeddedDiffEditorWidget, EmbeddedDiffEditorWidget2 } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedDiffEditorWidget2 } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IDiffEditorContribution } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -39,7 +39,7 @@ class DiffEditorHelperContribution extends Disposable implements IDiffEditorCont this._register(createScreenReaderHelp()); - const isEmbeddedDiffEditor = (this._diffEditor instanceof EmbeddedDiffEditorWidget) || (this._diffEditor instanceof EmbeddedDiffEditorWidget2); + const isEmbeddedDiffEditor = this._diffEditor instanceof EmbeddedDiffEditorWidget2; if (!isEmbeddedDiffEditor) { const computationResult = observableFromEvent(e => this._diffEditor.onDidUpdateDiff(e), () => this._diffEditor.getDiffComputationResult()); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 7ab77c57846..369eef0d3cf 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -10,7 +10,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { DiffElementViewModelBase, getFormattedMetadataJSON, getFormattedOutputJSON, OutputComparison, outputEqual, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffElementViewModel'; import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { CellEditType, CellUri, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -42,6 +41,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { fixedDiffEditorOptions, fixedEditorOptions, fixedEditorPadding } from 'vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { return { @@ -242,7 +242,7 @@ abstract class AbstractElementRenderer extends Disposable { protected _metadataInfoContainer!: HTMLElement; protected _metadataEditorContainer?: HTMLElement; protected _metadataEditorDisposeStore!: DisposableStore; - protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget; + protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget2; protected _outputHeaderContainer!: HTMLElement; protected _outputHeader!: PropertyHeader; @@ -256,8 +256,8 @@ abstract class AbstractElementRenderer extends Disposable { protected _outputLeftView?: OutputContainer; protected _outputRightView?: OutputContainer; protected _outputEditorDisposeStore!: DisposableStore; - protected _outputEditor?: CodeEditorWidget | DiffEditorWidget; - protected _outputMetadataEditor?: DiffEditorWidget; + protected _outputEditor?: CodeEditorWidget | DiffEditorWidget2; + protected _outputMetadataEditor?: DiffEditorWidget2; protected _diffEditorContainer!: HTMLElement; protected _diagonalFill?: HTMLElement; @@ -493,7 +493,7 @@ abstract class AbstractElementRenderer extends Disposable { this._metadataEditorDisposeStore.clear(); if (this.cell instanceof SideBySideDiffElementViewModel) { - this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { + this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget2, this._metadataEditorContainer!, { ...fixedDiffEditorOptions, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: false, @@ -606,7 +606,7 @@ abstract class AbstractElementRenderer extends Disposable { const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; const lineCount = Math.max(originalModel.getLineCount(), modifiedModel.getLineCount()); - this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputEditorContainer!, { + this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget2, this._outputEditorContainer!, { ...fixedDiffEditorOptions, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: true, @@ -1214,7 +1214,7 @@ export class InsertElement extends SingleSideDiffElement { } export class ModifiedElement extends AbstractElementRenderer { - private _editor?: DiffEditorWidget; + private _editor?: DiffEditorWidget2; private _editorViewStateChanged: boolean; private _editorContainer!: HTMLElement; private _inputToolbarContainer!: HTMLElement; @@ -1416,7 +1416,7 @@ export class ModifiedElement extends AbstractElementRenderer { this._outputMetadataContainer.style.top = `${this.cell.layoutInfo.rawOutputHeight}px`; // single output, metadata change, let's render a diff editor for metadata - this._outputMetadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputMetadataContainer!, { + this._outputMetadataEditor = this.instantiationService.createInstance(DiffEditorWidget2, this._outputMetadataContainer!, { ...fixedDiffEditorOptions, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: true, diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts index 0dd0c906d17..bd56f8c7ff4 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts @@ -8,7 +8,7 @@ import { hash } from 'vs/base/common/hash'; import { toFormattedString } from 'vs/base/common/jsonFormatter'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { fixedEditorPadding } from 'vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions'; @@ -341,10 +341,10 @@ export abstract class DiffElementViewModelBase extends Disposable { getComputedCellContainerWidth(layoutInfo: NotebookLayoutInfo, diffEditor: boolean, fullWidth: boolean) { if (fullWidth) { - return layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0) - 2; + return layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget2.ENTIRE_DIFF_OVERVIEW_WIDTH : 0) - 2; } - return (layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)) / 2 - 18 - 2; + return (layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget2.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)) / 2 - 18 - 2; } getOutputEditorViewState(): editorCommon.ICodeEditorViewState | editorCommon.IDiffEditorViewState | null { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 7148cccf5a3..62601268ebd 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -10,12 +10,12 @@ import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; export enum DiffSide { Original = 0, @@ -85,7 +85,7 @@ export interface CellDiffSideBySideRenderTemplate extends CellDiffCommonRenderTe readonly body: HTMLElement; readonly diffEditorContainer: HTMLElement; readonly elementDisposables: DisposableStore; - readonly sourceEditor: DiffEditorWidget; + readonly sourceEditor: DiffEditorWidget2; readonly editorContainer: HTMLElement; readonly inputToolbarContainer: HTMLElement; readonly toolbar: WorkbenchToolBar; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index 7cff07828c4..af0bb4a20f4 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -18,7 +18,7 @@ import { DiffElementViewModelBase, SideBySideDiffElementViewModel, SingleSideDif import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; import { DeletedElement, getOptimizedNestedCodeEditorWidgetOptions, InsertElement, ModifiedElement } from 'vs/workbench/contrib/notebook/browser/diff/diffComponents'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; +import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -234,7 +234,7 @@ export class CellDiffSideBySideRenderer implements IListRenderer Date: Mon, 4 Sep 2023 18:37:18 +0200 Subject: [PATCH 485/607] Polishs arraysFind --- src/vs/base/common/arrays.ts | 116 +--------------- src/vs/base/common/arraysFind.ts | 129 +++++++++++++++--- src/vs/base/test/common/arrays.test.ts | 21 +-- .../diffEditorWidget2/diffEditorWidget2.ts | 2 +- .../diffEditorWidget2/movedBlocksLines.ts | 3 +- .../editor/common/cursor/cursorCollection.ts | 5 +- .../common/model/guidesTextModelPart.ts | 2 +- .../editor/contrib/find/browser/findModel.ts | 4 +- .../folding/browser/hiddenRangeModel.ts | 4 +- .../browser/inlineCompletionsModel.ts | 4 +- .../suggestWidgetInlineCompletionProvider.ts | 5 +- .../browser/gotoSymbolQuickAccess.ts | 2 +- src/vs/workbench/api/common/extHostTesting.ts | 6 +- .../comments/browser/commentsController.ts | 5 +- .../contrib/debug/common/debugModel.ts | 5 +- .../mergeEditor/browser/model/mapping.ts | 3 +- .../mergeEditor/browser/view/viewModel.ts | 2 +- .../browser/contrib/find/findModel.ts | 10 +- .../browser/diff/notebookDiffEditor.ts | 4 +- .../testing/browser/testingExplorerView.ts | 4 +- .../testing/common/testResultService.ts | 4 +- 21 files changed, 164 insertions(+), 176 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 4061404c8ee..697eb769143 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -6,6 +6,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationError } from 'vs/base/common/errors'; import { ISplice } from 'vs/base/common/sequence'; +import { findFirstIdxMonotonousOrArrLen } from './arraysFind'; /** * Returns the last element of an array. @@ -106,27 +107,6 @@ export function binarySearch2(length: number, compareToKey: (index: number) => n return -(low + 1); } -/** - * Takes a sorted array and a function p. The array is sorted in such a way that all elements where p(x) is false - * are located before all elements where p(x) is true. - * @returns the least x for which p(x) is true or array.length if no element fullfills the given function. - */ -export function findFirstInSorted(array: ReadonlyArray, p: (x: T) => boolean): number { - let low = 0, high = array.length; - if (high === 0) { - return 0; // no children - } - while (low < high) { - const mid = Math.floor((low + high) / 2); - if (p(array[mid])) { - high = mid; - } else { - low = mid + 1; - } - } - return low; -} - type Compare = (a: T, b: T) => number; @@ -345,7 +325,7 @@ function topStep(array: ReadonlyArray, compare: (a: T, b: T) => number, re const element = array[i]; if (compare(element, result[n - 1]) < 0) { result.pop(); - const j = findFirstInSorted(result, e => compare(element, e) < 0); + const j = findFirstIdxMonotonousOrArrLen(result, e => compare(element, e) < 0); result.splice(j, 0, element); } } @@ -427,26 +407,6 @@ export function uniqueFilter(keyFn: (t: T) => R): (t: T) => boolean { }; } -export function findLast(arr: readonly T[], predicate: (item: T) => boolean): T | undefined { - const idx = findLastIndex(arr, predicate); - if (idx === -1) { - return undefined; - } - return arr[idx]; -} - -export function findLastIndex(array: ReadonlyArray, fn: (item: T) => boolean): number { - for (let i = array.length - 1; i >= 0; i--) { - const element = array[i]; - - if (fn(element)) { - return i; - } - } - - return -1; -} - export function firstOrDefault(array: ReadonlyArray, notFoundValue: NotFound): T | NotFound; export function firstOrDefault(array: ReadonlyArray): T | undefined; export function firstOrDefault(array: ReadonlyArray, notFoundValue?: NotFound): T | NotFound | undefined { @@ -622,20 +582,6 @@ export function getRandomElement(arr: T[]): T | undefined { return arr[Math.floor(Math.random() * arr.length)]; } -/** - * Returns the first mapped value of the array which is not undefined. - */ -export function mapFind(array: Iterable, mapFn: (value: T) => R | undefined): R | undefined { - for (const value of array) { - const mapped = mapFn(value); - if (mapped !== undefined) { - return mapped; - } - } - - return undefined; -} - /** * Insert the new items in the array. * @param array The original array. @@ -747,64 +693,6 @@ export function reverseOrder(comparator: Comparator): Comparator -comparator(a, b); } -/** - * Returns the first item that is equal to or greater than every other item. -*/ -export function findMaxBy(items: readonly T[], comparator: Comparator): T | undefined { - if (items.length === 0) { - return undefined; - } - - let max = items[0]; - for (let i = 1; i < items.length; i++) { - const item = items[i]; - if (comparator(item, max) > 0) { - max = item; - } - } - return max; -} - -/** - * Returns the last item that is equal to or greater than every other item. -*/ -export function findLastMaxBy(items: readonly T[], comparator: Comparator): T | undefined { - if (items.length === 0) { - return undefined; - } - - let max = items[0]; - for (let i = 1; i < items.length; i++) { - const item = items[i]; - if (comparator(item, max) >= 0) { - max = item; - } - } - return max; -} - -/** - * Returns the first item that is equal to or less than every other item. -*/ -export function findMinBy(items: readonly T[], comparator: Comparator): T | undefined { - return findMaxBy(items, (a, b) => -comparator(a, b)); -} - -export function findMaxIdxBy(items: readonly T[], comparator: Comparator): number { - if (items.length === 0) { - return -1; - } - - let maxIdx = 0; - for (let i = 1; i < items.length; i++) { - const item = items[i]; - if (comparator(item, items[maxIdx]) > 0) { - maxIdx = i; - } - } - return maxIdx; -} - export class ArrayQueue { private firstIdx = 0; private lastIdx = this.items.length - 1; diff --git a/src/vs/base/common/arraysFind.ts b/src/vs/base/common/arraysFind.ts index d40aba8ab67..8ef2d2ad936 100644 --- a/src/vs/base/common/arraysFind.ts +++ b/src/vs/base/common/arraysFind.ts @@ -3,15 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Comparator } from './arrays'; + +export function findLast(array: readonly T[], predicate: (item: T) => boolean): T | undefined { + const idx = findLastIdx(array, predicate); + if (idx === -1) { + return undefined; + } + return array[idx]; +} + +export function findLastIdx(array: readonly T[], predicate: (item: T) => boolean): number { + for (let i = array.length - 1; i >= 0; i--) { + const element = array[i]; + + if (predicate(element)) { + return i; + } + } + + return -1; +} + /** * Finds the last item where predicate is true using binary search. * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[true, ..., true, false, ..., false]`! * * @returns `undefined` if no item matches, otherwise the last item that matches the predicate. */ -export function findLastMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { - const idx = findLastIdxMonotonous(arr, predicate); - return idx === -1 ? undefined : arr[idx]; +export function findLastMonotonous(array: readonly T[], predicate: (item: T) => boolean): T | undefined { + const idx = findLastIdxMonotonous(array, predicate); + return idx === -1 ? undefined : array[idx]; } /** @@ -20,12 +42,12 @@ export function findLastMonotonous(arr: T[], predicate: (item: T) => boolean) * * @returns `startIdx - 1` if predicate is false for all items, otherwise the index of the last item that matches the predicate. */ -export function findLastIdxMonotonous(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number { +export function findLastIdxMonotonous(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number { let i = startIdx; let j = endIdxEx; while (i < j) { const k = Math.floor((i + j) / 2); - if (predicate(arr[k])) { + if (predicate(array[k])) { i = k + 1; } else { j = k; @@ -34,16 +56,15 @@ export function findLastIdxMonotonous(arr: T[], predicate: (item: T) => boole return i - 1; } - /** * Finds the first item where predicate is true using binary search. * `predicate` must be monotonous, i.e. `arr.map(predicate)` must be like `[false, ..., false, true, ..., true]`! * * @returns `undefined` if no item matches, otherwise the first item that matches the predicate. */ -export function findFirstMonotonous(arr: T[], predicate: (item: T) => boolean): T | undefined { - const idx = findFirstIdxMonotonousOrArrLen(arr, predicate); - return idx === arr.length ? undefined : arr[idx]; +export function findFirstMonotonous(array: readonly T[], predicate: (item: T) => boolean): T | undefined { + const idx = findFirstIdxMonotonousOrArrLen(array, predicate); + return idx === array.length ? undefined : array[idx]; } /** @@ -52,12 +73,12 @@ export function findFirstMonotonous(arr: T[], predicate: (item: T) => boolean * * @returns `endIdxEx` if predicate is false for all items, otherwise the index of the first item that matches the predicate. */ -export function findFirstIdxMonotonousOrArrLen(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number { +export function findFirstIdxMonotonousOrArrLen(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number { let i = startIdx; let j = endIdxEx; while (i < j) { const k = Math.floor((i + j) / 2); - if (predicate(arr[k])) { + if (predicate(array[k])) { j = k; } else { i = k + 1; @@ -66,9 +87,9 @@ export function findFirstIdxMonotonousOrArrLen(arr: T[], predicate: (item: T) return i; } -export function findFirstIdxMonotonous(arr: T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = arr.length): number { - const idx = findFirstIdxMonotonousOrArrLen(arr, predicate, startIdx, endIdxEx); - return idx === arr.length ? -1 : idx; +export function findFirstIdxMonotonous(array: readonly T[], predicate: (item: T) => boolean, startIdx = 0, endIdxEx = array.length): number { + const idx = findFirstIdxMonotonousOrArrLen(array, predicate, startIdx, endIdxEx); + return idx === array.length ? -1 : idx; } /** @@ -83,7 +104,7 @@ export class MonotonousArray { private _findLastMonotonousLastIdx = 0; private _prevFindLastPredicate: ((item: T) => boolean) | undefined; - constructor(private readonly _items: T[]) { + constructor(private readonly _array: readonly T[]) { } /** @@ -93,7 +114,7 @@ export class MonotonousArray { findLastMonotonous(predicate: (item: T) => boolean): T | undefined { if (MonotonousArray.assertInvariants) { if (this._prevFindLastPredicate) { - for (const item of this._items) { + for (const item of this._array) { if (this._prevFindLastPredicate(item) && !predicate(item)) { throw new Error('MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.'); } @@ -102,8 +123,80 @@ export class MonotonousArray { this._prevFindLastPredicate = predicate; } - const idx = findLastIdxMonotonous(this._items, predicate, this._findLastMonotonousLastIdx); + const idx = findLastIdxMonotonous(this._array, predicate, this._findLastMonotonousLastIdx); this._findLastMonotonousLastIdx = idx + 1; - return idx === -1 ? undefined : this._items[idx]; + return idx === -1 ? undefined : this._array[idx]; } } + +/** + * Returns the first item that is equal to or greater than every other item. +*/ +export function findFirstMaxBy(array: readonly T[], comparator: Comparator): T | undefined { + if (array.length === 0) { + return undefined; + } + + let max = array[0]; + for (let i = 1; i < array.length; i++) { + const item = array[i]; + if (comparator(item, max) > 0) { + max = item; + } + } + return max; +} + +/** + * Returns the last item that is equal to or greater than every other item. +*/ +export function findLastMaxBy(array: readonly T[], comparator: Comparator): T | undefined { + if (array.length === 0) { + return undefined; + } + + let max = array[0]; + for (let i = 1; i < array.length; i++) { + const item = array[i]; + if (comparator(item, max) >= 0) { + max = item; + } + } + return max; +} + +/** + * Returns the first item that is equal to or less than every other item. +*/ +export function findFirstMinBy(array: readonly T[], comparator: Comparator): T | undefined { + return findFirstMaxBy(array, (a, b) => -comparator(a, b)); +} + +export function findMaxIdxBy(array: readonly T[], comparator: Comparator): number { + if (array.length === 0) { + return -1; + } + + let maxIdx = 0; + for (let i = 1; i < array.length; i++) { + const item = array[i]; + if (comparator(item, array[maxIdx]) > 0) { + maxIdx = i; + } + } + return maxIdx; +} + +/** + * Returns the first mapped value of the array which is not undefined. + */ +export function mapFindFirst(items: Iterable, mapFn: (value: T) => R | undefined): R | undefined { + for (const value of items) { + const mapped = mapFn(value); + if (mapped !== undefined) { + return mapped; + } + } + + return undefined; +} diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index d49357f051a..d4bd7850703 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as arrays from 'vs/base/common/arrays'; +import * as arraysFind from 'vs/base/common/arraysFind'; suite('Arrays', () => { @@ -22,25 +23,25 @@ suite('Arrays', () => { test('findFirst', () => { const array = [1, 4, 5, 7, 55, 59, 60, 61, 64, 69]; - let idx = arrays.findFirstInSorted(array, e => e >= 0); + let idx = arraysFind.findFirstIdxMonotonousOrArrLen(array, e => e >= 0); assert.strictEqual(array[idx], 1); - idx = arrays.findFirstInSorted(array, e => e > 1); + idx = arraysFind.findFirstIdxMonotonousOrArrLen(array, e => e > 1); assert.strictEqual(array[idx], 4); - idx = arrays.findFirstInSorted(array, e => e >= 8); + idx = arraysFind.findFirstIdxMonotonousOrArrLen(array, e => e >= 8); assert.strictEqual(array[idx], 55); - idx = arrays.findFirstInSorted(array, e => e >= 61); + idx = arraysFind.findFirstIdxMonotonousOrArrLen(array, e => e >= 61); assert.strictEqual(array[idx], 61); - idx = arrays.findFirstInSorted(array, e => e >= 69); + idx = arraysFind.findFirstIdxMonotonousOrArrLen(array, e => e >= 69); assert.strictEqual(array[idx], 69); - idx = arrays.findFirstInSorted(array, e => e >= 70); + idx = arraysFind.findFirstIdxMonotonousOrArrLen(array, e => e >= 70); assert.strictEqual(idx, array.length); - idx = arrays.findFirstInSorted([], e => e >= 0); + idx = arraysFind.findFirstIdxMonotonousOrArrLen([], e => e >= 0); assert.strictEqual(array[idx], 1); }); @@ -372,7 +373,7 @@ suite('Arrays', () => { const array = [{ v: 3 }, { v: 5 }, { v: 2 }, { v: 2 }, { v: 2 }, { v: 5 }]; assert.strictEqual( - array.indexOf(arrays.findMaxBy(array, arrays.compareBy(v => v.v, arrays.numberComparator))!), + array.indexOf(arraysFind.findFirstMaxBy(array, arrays.compareBy(v => v.v, arrays.numberComparator))!), 1 ); }); @@ -381,7 +382,7 @@ suite('Arrays', () => { const array = [{ v: 3 }, { v: 5 }, { v: 2 }, { v: 2 }, { v: 2 }, { v: 5 }]; assert.strictEqual( - array.indexOf(arrays.findLastMaxBy(array, arrays.compareBy(v => v.v, arrays.numberComparator))!), + array.indexOf(arraysFind.findLastMaxBy(array, arrays.compareBy(v => v.v, arrays.numberComparator))!), 5 ); }); @@ -390,7 +391,7 @@ suite('Arrays', () => { const array = [{ v: 3 }, { v: 5 }, { v: 2 }, { v: 2 }, { v: 2 }, { v: 5 }]; assert.strictEqual( - array.indexOf(arrays.findMinBy(array, arrays.compareBy(v => v.v, arrays.numberComparator))!), + array.indexOf(arraysFind.findFirstMinBy(array, arrays.compareBy(v => v.v, arrays.numberComparator))!), 2 ); }); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts index 264b8117601..0e169daeb7b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { $, h } from 'vs/base/browser/dom'; import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; -import { findLast } from 'vs/base/common/arrays'; +import { findLast } from 'vs/base/common/arraysFind'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { toDisposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts index c8ed76eb54c..54e19c99653 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts @@ -6,7 +6,8 @@ import { h } from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action } from 'vs/base/common/actions'; -import { booleanComparator, compareBy, findMaxIdxBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; +import { booleanComparator, compareBy, numberComparator, tieBreakComparators } from 'vs/base/common/arrays'; +import { findMaxIdxBy } from 'vs/base/common/arraysFind'; import { Codicon } from 'vs/base/common/codicons'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, autorunHandleChanges, autorunWithStore, constObservable, derived, derivedWithStore, observableFromEvent, observableSignalFromEvent, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; diff --git a/src/vs/editor/common/cursor/cursorCollection.ts b/src/vs/editor/common/cursor/cursorCollection.ts index c29f268a969..f2e8a8b4388 100644 --- a/src/vs/editor/common/cursor/cursorCollection.ts +++ b/src/vs/editor/common/cursor/cursorCollection.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareBy, findLastMaxBy, findMinBy } from 'vs/base/common/arrays'; +import { compareBy } from 'vs/base/common/arrays'; +import { findLastMaxBy, findFirstMinBy } from 'vs/base/common/arraysFind'; import { CursorState, PartialCursorState } from 'vs/editor/common/cursorCommon'; import { CursorContext } from 'vs/editor/common/cursor/cursorContext'; import { Cursor } from 'vs/editor/common/cursor/oneCursor'; @@ -72,7 +73,7 @@ export class CursorCollection { } public getTopMostViewPosition(): Position { - return findMinBy( + return findFirstMinBy( this.cursors, compareBy(c => c.viewState.position, Position.compare) )!.viewState.position; diff --git a/src/vs/editor/common/model/guidesTextModelPart.ts b/src/vs/editor/common/model/guidesTextModelPart.ts index d8e264475ac..7963063d044 100644 --- a/src/vs/editor/common/model/guidesTextModelPart.ts +++ b/src/vs/editor/common/model/guidesTextModelPart.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findLast } from 'vs/base/common/arrays'; +import { findLast } from 'vs/base/common/arraysFind'; import * as strings from 'vs/base/common/strings'; import { CursorColumns } from 'vs/editor/common/core/cursorColumns'; import { IPosition, Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/editor/contrib/find/browser/findModel.ts b/src/vs/editor/contrib/find/browser/findModel.ts index 8cdb9cbd804..a4611d37daf 100644 --- a/src/vs/editor/contrib/find/browser/findModel.ts +++ b/src/vs/editor/contrib/find/browser/findModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findFirstInSorted } from 'vs/base/common/arrays'; +import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { RunOnceScheduler, TimeoutTimer } from 'vs/base/common/async'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; @@ -211,7 +211,7 @@ export class FindModelBoundToEditorModel { if (currentMatchesPosition === 0 && findMatches.length > 0) { // current selection is not on top of a match // try to find its nearest result from the top of the document - const matchAfterSelection = findFirstInSorted(findMatches.map(match => match.range), range => Range.compareRangesUsingStarts(range, editorSelection) >= 0); + const matchAfterSelection = findFirstIdxMonotonousOrArrLen(findMatches.map(match => match.range), range => Range.compareRangesUsingStarts(range, editorSelection) >= 0); currentMatchesPosition = matchAfterSelection > 0 ? matchAfterSelection - 1 + 1 /** match position is one based */ : currentMatchesPosition; } diff --git a/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts b/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts index ea8ff076531..6d72da79026 100644 --- a/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts +++ b/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findFirstInSorted } from 'vs/base/common/arrays'; +import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { Emitter, Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -141,7 +141,7 @@ function isInside(line: number, range: IRange) { return line >= range.startLineNumber && line <= range.endLineNumber; } function findRange(ranges: IRange[], line: number): IRange | null { - const i = findFirstInSorted(ranges, r => line < r.startLineNumber) - 1; + const i = findFirstIdxMonotonousOrArrLen(ranges, r => line < r.startLineNumber) - 1; if (i >= 0 && ranges[i].endLineNumber >= line) { return ranges[i]; } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts index e3e387ea66d..60d791a715d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { mapFind } from 'vs/base/common/arrays'; +import { mapFindFirst } from 'vs/base/common/arraysFind'; import { BugIndicatingError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ITransaction, autorun, derived, derivedHandleChanges, derivedOpts, recomputeInitiallyAndOnChange, observableSignal, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; @@ -244,7 +244,7 @@ export class InlineCompletionsModel extends Disposable { ? suggestWidgetInlineCompletions.inlineCompletions : [this.selectedInlineCompletion.read(reader)].filter(isDefined); - const augmentedCompletion = mapFind(candidateInlineCompletions, completion => { + const augmentedCompletion = mapFindFirst(candidateInlineCompletions, completion => { let r = completion.toSingleTextEdit(reader); r = r.removeCommonPrefix(model, Range.fromPositions(r.range.getStartPosition(), suggestCompletion.range.getEndPosition())); return r.augments(suggestCompletion) ? { edit: r, completion } : undefined; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts index 24c90dbdb52..90d53b26c8f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/suggestWidgetInlineCompletionProvider.ts @@ -16,7 +16,8 @@ import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestCont import { IObservable, ITransaction, observableValue, transaction } from 'vs/base/common/observable'; import { SingleTextEdit } from 'vs/editor/contrib/inlineCompletions/browser/singleTextEdit'; import { ITextModel } from 'vs/editor/common/model'; -import { compareBy, findMaxBy, numberComparator } from 'vs/base/common/arrays'; +import { compareBy, numberComparator } from 'vs/base/common/arrays'; +import { findFirstMaxBy } from 'vs/base/common/arraysFind'; export class SuggestWidgetAdaptor extends Disposable { private isSuggestWidgetVisible: boolean = false; @@ -80,7 +81,7 @@ export class SuggestWidgetAdaptor extends Disposable { }) .filter(item => item && item.valid && item.prefixLength > 0); - const result = findMaxBy( + const result = findFirstMaxBy( candidates, compareBy(s => s!.prefixLength, numberComparator) ); diff --git a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts index 77197c2f9d4..01092241b7a 100644 --- a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts @@ -21,7 +21,7 @@ import { localize } from 'vs/nls'; import { IQuickInputButton, IQuickPick, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { Position } from 'vs/editor/common/core/position'; -import { findLast } from 'vs/base/common/arrays'; +import { findLast } from 'vs/base/common/arraysFind'; export interface IGotoSymbolQuickPickItem extends IQuickPickItem { kind: SymbolKind; diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index 94af4f6b06e..b8096372fae 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -5,7 +5,7 @@ /* eslint-disable local/code-no-native-private */ -import { mapFind } from 'vs/base/common/arrays'; +import { mapFindFirst } from 'vs/base/common/arraysFind'; import { RunOnceScheduler } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -229,7 +229,7 @@ export class ExtHostTesting implements ExtHostTestingShape { * @inheritdoc */ $provideFileCoverage(runId: string, taskId: string, token: CancellationToken): Promise { - const coverage = mapFind(this.runTracker.trackers, t => t.id === runId ? t.getCoverage(taskId) : undefined); + const coverage = mapFindFirst(this.runTracker.trackers, t => t.id === runId ? t.getCoverage(taskId) : undefined); return coverage?.provideFileCoverage(token) ?? Promise.resolve([]); } @@ -237,7 +237,7 @@ export class ExtHostTesting implements ExtHostTestingShape { * @inheritdoc */ $resolveFileCoverage(runId: string, taskId: string, fileIndex: number, token: CancellationToken): Promise { - const coverage = mapFind(this.runTracker.trackers, t => t.id === runId ? t.getCoverage(taskId) : undefined); + const coverage = mapFindFirst(this.runTracker.trackers, t => t.id === runId ? t.getCoverage(taskId) : undefined); return coverage?.resolveFileCoverage(fileIndex, token) ?? Promise.resolve([]); } diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 339b10df9e7..9fac6af0a65 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Action, IAction } from 'vs/base/common/actions'; -import { coalesce, findFirstInSorted } from 'vs/base/common/arrays'; +import { coalesce } from 'vs/base/common/arrays'; +import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -588,7 +589,7 @@ export class CommentController implements IEditorContribution { return 0; }); - const idx = findFirstInSorted(sortedWidgets, widget => { + const idx = findFirstIdxMonotonousOrArrLen(sortedWidgets, widget => { const lineValueOne = reverse ? after.lineNumber : (widget.commentThread.range?.startLineNumber ?? 0); const lineValueTwo = reverse ? (widget.commentThread.range?.startLineNumber ?? 0) : after.lineNumber; const columnValueOne = reverse ? after.column : (widget.commentThread.range?.startColumn ?? 0); diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 50640fcf7e5..15030ce7822 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { distinct, findLastIndex } from 'vs/base/common/arrays'; +import { distinct } from 'vs/base/common/arrays'; import { DeferredPromise, RunOnceScheduler } from 'vs/base/common/async'; import { decodeBase64, encodeBase64, VSBuffer } from 'vs/base/common/buffer'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -27,6 +27,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILogService } from 'vs/platform/log/common/log'; import { autorun } from 'vs/base/common/observable'; +import { findLastIdx } from 'vs/base/common/arraysFind'; interface IDebugProtocolVariableWithContext extends DebugProtocol.Variable { __vscodeVariableMenuContext?: string; @@ -1253,7 +1254,7 @@ export class DebugModel extends Disposable implements IDebugModel { let index = -1; if (session.parentSession) { // Make sure that child sessions are placed after the parent session - index = findLastIndex(this.sessions, s => s.parentSession === session.parentSession || s === session.parentSession); + index = findLastIdx(this.sessions, s => s.parentSession === session.parentSession || s === session.parentSession); } if (index >= 0) { this.sessions.splice(index + 1, 0, session); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts index 1f1bb9258b6..3bdcb016fef 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareBy, findLast, lastOrDefault, numberComparator } from 'vs/base/common/arrays'; +import { compareBy, lastOrDefault, numberComparator } from 'vs/base/common/arrays'; +import { findLast } from 'vs/base/common/arraysFind'; import { assertFn, checkAdjacentItems } from 'vs/base/common/assert'; import { BugIndicatingError } from 'vs/base/common/errors'; import { Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts index 36b94d8490b..ca8a00e6b56 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findLast } from 'vs/base/common/arrays'; +import { findLast } from 'vs/base/common/arraysFind'; import { Disposable } from 'vs/base/common/lifecycle'; import { derived, derivedObservableWithWritableCache, IObservable, IReader, ITransaction, observableValue, transaction } from 'vs/base/common/observable'; import { Range } from 'vs/editor/common/core/range'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts index da74cbb1a76..1d316007d01 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts @@ -12,7 +12,7 @@ import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contri import { CellKind, INotebookSearchOptions, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { findFirstInSorted } from 'vs/base/common/arrays'; +import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; @@ -341,7 +341,7 @@ export class FindModel extends Disposable { } const findFirstMatchAfterCellIndex = (cellIndex: number) => { - const matchAfterSelection = findFirstInSorted(findMatches.map(match => match.index), index => index >= cellIndex); + const matchAfterSelection = findFirstIdxMonotonousOrArrLen(findMatches.map(match => match.index), index => index >= cellIndex); this._updateCurrentMatch(findMatches, this._matchesCountBeforeIndex(findMatches, matchAfterSelection)); }; @@ -403,7 +403,7 @@ export class FindModel extends Disposable { return; } - const matchAfterSelection = findFirstInSorted(findMatches, match => match.index >= oldCurrMatchCellIndex) % findMatches.length; + const matchAfterSelection = findFirstIdxMonotonousOrArrLen(findMatches, match => match.index >= oldCurrMatchCellIndex) % findMatches.length; if (findMatches[matchAfterSelection].index > oldCurrMatchCellIndex) { // there is no search result in curr cell anymore, find the nearest one (from top to bottom) this._updateCurrentMatch(findMatches, this._matchesCountBeforeIndex(findMatches, matchAfterSelection)); @@ -419,7 +419,7 @@ export class FindModel extends Disposable { if (currMatchRangeInEditor !== null) { // we find a range for the previous current match, let's find the nearest one after it (can overlap) const cellMatch = findMatches[matchAfterSelection]; - const matchAfterOldSelection = findFirstInSorted(cellMatch.contentMatches, match => Range.compareRangesUsingStarts((match as FindMatch).range, currMatchRangeInEditor) >= 0); + const matchAfterOldSelection = findFirstIdxMonotonousOrArrLen(cellMatch.contentMatches, match => Range.compareRangesUsingStarts((match as FindMatch).range, currMatchRangeInEditor) >= 0); this._updateCurrentMatch(findMatches, this._matchesCountBeforeIndex(findMatches, matchAfterSelection) + matchAfterOldSelection); } else { // no range found, let's fall back to finding the nearest match @@ -429,7 +429,7 @@ export class FindModel extends Disposable { } } else { // output now has the highlight - const matchAfterSelection = findFirstInSorted(findMatches.map(match => match.index), index => index >= oldCurrMatchCellIndex) % findMatches.length; + const matchAfterSelection = findFirstIdxMonotonousOrArrLen(findMatches.map(match => match.index), index => index >= oldCurrMatchCellIndex) % findMatches.length; this._updateCurrentMatch(findMatches, this._matchesCountBeforeIndex(findMatches, matchAfterSelection)); } } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index d374f6caf15..955cf94c74e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; -import { findLastIndex } from 'vs/base/common/arrays'; +import { findLastIdx } from 'vs/base/common/arraysFind'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -825,7 +825,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.reveal(prevChangeIndex); } else { // go to the last one - const index = findLastIndex(this._diffElementViewModels, vm => vm.type !== 'unchanged'); + const index = findLastIdx(this._diffElementViewModels, vm => vm.type !== 'unchanged'); if (index >= 0) { this._list.setFocus([index]); this._list.reveal(index); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 84e09da8c8f..b0ad29a3597 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -12,7 +12,7 @@ import { IIdentityProvider, IKeyboardNavigationLabelProvider, IListVirtualDelega import { DefaultKeyboardNavigationDelegate, IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { ITreeContextMenuEvent, ITreeFilter, ITreeNode, ITreeRenderer, ITreeSorter, TreeFilterResult, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { Action, ActionRunner, IAction, Separator } from 'vs/base/common/actions'; -import { mapFind } from 'vs/base/common/arrays'; +import { mapFindFirst } from 'vs/base/common/arraysFind'; import { RunOnceScheduler, disposableTimeout } from 'vs/base/common/async'; import { Color, RGBA } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -509,7 +509,7 @@ class ResultSummaryView extends Disposable { rerun.style.display = 'none'; } else { const last = results[0]; - const dominantState = mapFind(statesInOrder, s => last.counts[s] > 0 ? s : undefined); + const dominantState = mapFindFirst(statesInOrder, s => last.counts[s] > 0 ? s : undefined); status.className = ThemeIcon.asClassName(icons.testingStatesToIcons.get(dominantState ?? TestResultState.Unset)!); counts = collectTestStateCounts(false, [last]); duration.textContent = last instanceof LiveTestResult ? formatDuration(last.completedAt! - last.startedAt) : ''; diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index ecd01614b4c..69c740dce7c 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findFirstInSorted } from 'vs/base/common/arrays'; +import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; @@ -168,7 +168,7 @@ export class TestResultService implements ITestResultService { if (result.completedAt === undefined) { this.results.unshift(result); } else { - const index = findFirstInSorted(this.results, r => r.completedAt !== undefined && r.completedAt <= result.completedAt!); + const index = findFirstIdxMonotonousOrArrLen(this.results, r => r.completedAt !== undefined && r.completedAt <= result.completedAt!); this.results.splice(index, 0, result); this.persistScheduler.schedule(); } From 280320786e58ef2023a3cd22de799883a8f479fd Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 5 Sep 2023 10:21:11 +0200 Subject: [PATCH 486/607] support not tabs tabheight --- .../browser/parts/editor/media/notabstitlecontrol.css | 6 +++--- .../browser/parts/editor/noTabsTitleControl.ts | 7 +++++++ .../browser/parts/editor/tabsTitleControl.ts | 11 ++--------- src/vs/workbench/browser/parts/editor/titleControl.ts | 10 +++++++++- src/vs/workbench/browser/workbench.contribution.ts | 2 +- src/vs/workbench/common/editor.ts | 2 +- 6 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 61440406d69..b737c53817c 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -6,7 +6,7 @@ /* Title Label */ .monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container { - height: 35px; + height: var(--tab-height); display: flex; justify-content: flex-start; align-items: center; @@ -15,7 +15,7 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container > .title-label { - line-height: 35px; + line-height: var(--tab-height); overflow: hidden; text-overflow: ellipsis; position: relative; @@ -94,7 +94,7 @@ flex: initial; opacity: 0.5; padding-right: 8px; - height: 35px; + height: var(--tab-height); } .monaco-workbench .part.editor > .content .editor-group-container > .title > .title-actions .action-item { diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index d60267cd22f..bc4e3b25e21 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -50,6 +50,8 @@ export class NoTabsTitleControl extends TitleControl { this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, undefined)).element; this._register(addDisposableListener(this.editorLabel.element, EventType.CLICK, e => this.onTitleLabelClick(e))); + this.updateTabHeight(); + // Breadcrumbs this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, widgetStyles: { ...defaultBreadcrumbsWidgetStyles, breadcrumbsBackground: Color.transparent.toString() }, showPlaceholder: false }); titleContainer.classList.toggle('breadcrumbs', Boolean(this.breadcrumbsControl)); @@ -198,6 +200,11 @@ export class NoTabsTitleControl extends TitleControl { } updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { + // Update tab height + if (oldOptions.tabHeight !== newOptions.tabHeight) { + this.updateTabHeight(); + } + if (oldOptions.labelFormat !== newOptions.labelFormat || !equals(oldOptions.decorations, newOptions.decorations)) { this.redraw(); } diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 153ba1472b6..e331f0e4945 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -268,10 +268,6 @@ export class TabsTitleControl extends TitleControl { }); } - private updateTabHeight(): void { - const tabsContainer = assertIsDefined(this.tabsContainer); - tabsContainer.style.setProperty('--tab-height', `${this.getTabHeight()}px`); - } private getTabsScrollbarSizing(): number { if (this.accessor.partOptions.titleScrollbarSizing !== 'large') { @@ -1558,7 +1554,7 @@ export class TabsTitleControl extends TitleControl { if (this.accessor.partOptions.wrapTabs && this.tabsAndActionsContainer?.classList.contains('wrapping')) { total = this.tabsAndActionsContainer.offsetHeight; } else { - total = this.getTabHeight(); + total = this.tabHeight; } const offset = total; @@ -1571,9 +1567,6 @@ export class TabsTitleControl extends TitleControl { return { total, offset }; } - getTabHeight() { - return this.accessor.partOptions.tabHeight !== 'small' ? 35 : 22; - } layout(dimensions: ITitleControlDimensions, options?: ITabsTitleControlLayoutOptions): Dimension { @@ -1720,7 +1713,7 @@ export class TabsTitleControl extends TitleControl { if (tabsWrapMultiLine) { if ( (tabsContainer.offsetHeight > dimensions.available.height) || // if height exceeds available height - (allTabsWidth === visibleTabsWidth && tabsContainer.offsetHeight === this.getTabHeight()) || // if wrapping is not needed anymore + (allTabsWidth === visibleTabsWidth && tabsContainer.offsetHeight === this.tabHeight) || // if wrapping is not needed anymore (!lastTabFitsWrapped()) // if last tab does not fit anymore ) { updateTabsWrapping(false); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index e020cd99372..352258bb80a 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -115,7 +115,7 @@ export abstract class TitleControl extends Themable { private renderDropdownAsChildElement: boolean; constructor( - parent: HTMLElement, + protected parent: HTMLElement, protected accessor: IEditorGroupsAccessor, protected group: IEditorGroupView, @IContextMenuService protected readonly contextMenuService: IContextMenuService, @@ -422,6 +422,14 @@ export abstract class TitleControl extends Themable { return keybinding ? keybinding.getLabel() ?? undefined : undefined; } + protected get tabHeight() { + return this.accessor.partOptions.tabHeight !== 'compact' ? 35 : 22; + } + + protected updateTabHeight(): void { + this.parent.style.setProperty('--tab-height', `${this.tabHeight}px`); + } + abstract openEditor(editor: EditorInput): void; abstract openEditors(editors: EditorInput[]): void; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 85d699dfbf5..1a2d836c0a5 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -167,7 +167,7 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'workbench.editor.tabHeight': { 'type': 'string', - 'enum': ['normal', 'small'], + 'enum': ['normal', 'compact'], 'default': 'normal', 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.editor.tabHeight' }, "Controls the height of editor tabs.") }, diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 9414e6004df..d38c979a477 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -1098,7 +1098,7 @@ interface IEditorPartConfiguration { tabSizingFixedMinWidth?: number; tabSizingFixedMaxWidth?: number; pinnedTabSizing?: 'normal' | 'compact' | 'shrink'; - tabHeight?: 'normal' | 'small'; + tabHeight?: 'normal' | 'compact'; preventPinnedEditorClose?: PreventPinnedEditorClose; titleScrollbarSizing?: 'default' | 'large'; focusRecentEditorAfterClose?: boolean; From 4509267f8993d315e8dd40554f673f1f6d4af1d5 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 5 Sep 2023 10:56:53 +0200 Subject: [PATCH 487/607] modify tabheight description --- 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 1a2d836c0a5..b3a23711702 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -169,7 +169,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'type': 'string', 'enum': ['normal', 'compact'], 'default': 'normal', - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.editor.tabHeight' }, "Controls the height of editor tabs.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'workbench.editor.tabHeight' }, "Controls the height of editor tabs. Also applies to the title control bar when `#workbench.editor.showTabs#` is disabled.") }, 'workbench.editor.pinnedTabSizing': { 'type': 'string', From 5463b416d2c411d0df33668e25ea01fda2b45fca Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Sep 2023 11:13:44 +0200 Subject: [PATCH 488/607] Move marker creation to MarkerDecorations --- .../services/markerDecorationsService.ts | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/src/vs/editor/common/services/markerDecorationsService.ts b/src/vs/editor/common/services/markerDecorationsService.ts index 080e870b8b7..935b479a7f8 100644 --- a/src/vs/editor/common/services/markerDecorationsService.ts +++ b/src/vs/editor/common/services/markerDecorationsService.ts @@ -34,7 +34,13 @@ class MarkerDecorations extends Disposable { })); } - public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): boolean { + public update(markers: IMarker[]): boolean { + const newDecorations: IModelDeltaDecoration[] = markers.map((marker) => { + return { + range: this._createDecorationRange(this.model, marker), + options: this._createDecorationOption(marker) + }; + }); const oldIds = [...this._markersData.keys()]; this._markersData.clear(); const ids = this.model.deltaDecorations(oldIds, newDecorations); @@ -58,87 +64,6 @@ class MarkerDecorations extends Disposable { }); return res; } -} - -export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService { - - declare readonly _serviceBrand: undefined; - - private readonly _onDidChangeMarker = this._register(new Emitter()); - readonly onDidChangeMarker: Event = this._onDidChangeMarker.event; - - private readonly _markerDecorations = new ResourceMap(); - - constructor( - @IModelService modelService: IModelService, - @IMarkerService private readonly _markerService: IMarkerService - ) { - super(); - modelService.getModels().forEach(model => this._onModelAdded(model)); - this._register(modelService.onModelAdded(this._onModelAdded, this)); - this._register(modelService.onModelRemoved(this._onModelRemoved, this)); - this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this)); - } - - override dispose() { - super.dispose(); - this._markerDecorations.forEach(value => value.dispose()); - this._markerDecorations.clear(); - } - - getMarker(uri: URI, decoration: IModelDecoration): IMarker | null { - const markerDecorations = this._markerDecorations.get(uri); - return markerDecorations ? (markerDecorations.getMarker(decoration) || null) : null; - } - - getLiveMarkers(uri: URI): [Range, IMarker][] { - const markerDecorations = this._markerDecorations.get(uri); - return markerDecorations ? markerDecorations.getMarkers() : []; - } - - private _handleMarkerChange(changedResources: readonly URI[]): void { - changedResources.forEach((resource) => { - const markerDecorations = this._markerDecorations.get(resource); - if (markerDecorations) { - this._updateDecorations(markerDecorations); - } - }); - } - - private _onModelAdded(model: ITextModel): void { - const markerDecorations = new MarkerDecorations(model); - this._markerDecorations.set(model.uri, markerDecorations); - this._updateDecorations(markerDecorations); - } - - private _onModelRemoved(model: ITextModel): void { - const markerDecorations = this._markerDecorations.get(model.uri); - if (markerDecorations) { - markerDecorations.dispose(); - this._markerDecorations.delete(model.uri); - } - - // clean up markers for internal, transient models - if (model.uri.scheme === Schemas.inMemory - || model.uri.scheme === Schemas.internal - || model.uri.scheme === Schemas.vscode) { - this._markerService?.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); - } - } - - private _updateDecorations(markerDecorations: MarkerDecorations): void { - // Limit to the first 500 errors/warnings - const markers = this._markerService.read({ resource: markerDecorations.model.uri, take: 500 }); - const newModelDecorations: IModelDeltaDecoration[] = markers.map((marker) => { - return { - range: this._createDecorationRange(markerDecorations.model, marker), - options: this._createDecorationOption(marker) - }; - }); - if (markerDecorations.update(markers, newModelDecorations)) { - this._onDidChangeMarker.fire(markerDecorations.model); - } - } private _createDecorationRange(model: ITextModel, rawMarker: IMarker): Range { @@ -252,3 +177,78 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor return false; } } + +export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidChangeMarker = this._register(new Emitter()); + readonly onDidChangeMarker: Event = this._onDidChangeMarker.event; + + private readonly _markerDecorations = new ResourceMap(); + + constructor( + @IModelService modelService: IModelService, + @IMarkerService private readonly _markerService: IMarkerService + ) { + super(); + modelService.getModels().forEach(model => this._onModelAdded(model)); + this._register(modelService.onModelAdded(this._onModelAdded, this)); + this._register(modelService.onModelRemoved(this._onModelRemoved, this)); + this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this)); + } + + override dispose() { + super.dispose(); + this._markerDecorations.forEach(value => value.dispose()); + this._markerDecorations.clear(); + } + + getMarker(uri: URI, decoration: IModelDecoration): IMarker | null { + const markerDecorations = this._markerDecorations.get(uri); + return markerDecorations ? (markerDecorations.getMarker(decoration) || null) : null; + } + + getLiveMarkers(uri: URI): [Range, IMarker][] { + const markerDecorations = this._markerDecorations.get(uri); + return markerDecorations ? markerDecorations.getMarkers() : []; + } + + private _handleMarkerChange(changedResources: readonly URI[]): void { + changedResources.forEach((resource) => { + const markerDecorations = this._markerDecorations.get(resource); + if (markerDecorations) { + this._updateDecorations(markerDecorations); + } + }); + } + + private _onModelAdded(model: ITextModel): void { + const markerDecorations = new MarkerDecorations(model); + this._markerDecorations.set(model.uri, markerDecorations); + this._updateDecorations(markerDecorations); + } + + private _onModelRemoved(model: ITextModel): void { + const markerDecorations = this._markerDecorations.get(model.uri); + if (markerDecorations) { + markerDecorations.dispose(); + this._markerDecorations.delete(model.uri); + } + + // clean up markers for internal, transient models + if (model.uri.scheme === Schemas.inMemory + || model.uri.scheme === Schemas.internal + || model.uri.scheme === Schemas.vscode) { + this._markerService?.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); + } + } + + private _updateDecorations(markerDecorations: MarkerDecorations): void { + // Limit to the first 500 errors/warnings + const markers = this._markerService.read({ resource: markerDecorations.model.uri, take: 500 }); + if (markerDecorations.update(markers)) { + this._onDidChangeMarker.fire(markerDecorations.model); + } + } +} From 8f4d4d4bad51c7af90975521ad44fb1c82f3d911 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 5 Sep 2023 11:47:13 +0200 Subject: [PATCH 489/607] workaround browser bug, fix browser test debugging --- src/vs/base/common/arrays.ts | 6 +++++- test/unit/browser/renderer.html | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index 4061404c8ee..ad2f1b67137 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -667,7 +667,11 @@ export function insertInto(array: T[], start: number, newItems: T[]): void { */ export function splice(array: T[], start: number, deleteCount: number, newItems: T[]): T[] { const index = getActualStartIndex(array, start); - const result = array.splice(index, deleteCount); + let result = array.splice(index, deleteCount); + if (result === undefined) { + // see https://bugs.webkit.org/show_bug.cgi?id=261140 + result = []; + } insertInto(array, index, newItems); return result; } diff --git a/test/unit/browser/renderer.html b/test/unit/browser/renderer.html index 45786072900..fe62a749a68 100644 --- a/test/unit/browser/renderer.html +++ b/test/unit/browser/renderer.html @@ -156,7 +156,7 @@ if (Array.isArray(modules) && modules.length > 0) { console.log('MANUALLY running tests', modules); - loadAndRun(modules, true).then(() => console.log('done'), err => console.log(err)); + loadAndRun({modules}, true).then(() => console.log('done'), err => console.log(err)); } From ba3bc87570dd69a027df091b59e7dfa8e9e7f0bb Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 5 Sep 2023 11:53:37 +0200 Subject: [PATCH 490/607] unskip test --- src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index 6b60b35aef6..9ae0e08b0f1 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -380,7 +380,7 @@ suite('IndexTreeModel', () => { assert.deepStrictEqual(list[5].depth, 1); })); - test.skip('smart diff consistency', () => { + test('smart diff consistency', () => { const times = 500; const minEdits = 1; const maxEdits = 10; From 9c04ba42a86d04141e6a5290eb9ab398f2e12658 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 5 Sep 2023 11:58:10 +0200 Subject: [PATCH 491/607] Fixes #159555: Only recreate decorations for changed markers --- src/vs/base/common/map.ts | 60 ++++++ src/vs/base/test/common/map.test.ts | 88 ++++++++- .../services/markerDecorationsService.ts | 187 ++++++++++-------- 3 files changed, 247 insertions(+), 88 deletions(-) diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index e8dc62329e3..6f85a3111e3 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -664,3 +664,63 @@ export class CounterSet { return this.map.has(value); } } + +/** + * A map that allows access both by keys and values. + * **NOTE**: values need to be unique. + */ +export class BidirectionalMap { + + private readonly _m1 = new Map(); + private readonly _m2 = new Map(); + + constructor(entries?: readonly (readonly [K, V])[]) { + if (entries) { + for (const [key, value] of entries) { + this.set(key, value); + } + } + } + + clear(): void { + this._m1.clear(); + this._m2.clear(); + } + + set(key: K, value: V): void { + this._m1.set(key, value); + this._m2.set(value, key); + } + + get(key: K): V | undefined { + return this._m1.get(key); + } + + getKey(value: V): K | undefined { + return this._m2.get(value); + } + + delete(key: K): boolean { + const value = this._m1.get(key); + if (value === undefined) { + return false; + } + this._m1.delete(key); + this._m2.delete(value); + return true; + } + + forEach(callbackfn: (value: V, key: K, map: BidirectionalMap) => void, thisArg?: any): void { + this._m1.forEach((value, key) => { + callbackfn.call(thisArg, value, key, this); + }); + } + + keys(): IterableIterator { + return this._m1.keys(); + } + + values(): IterableIterator { + return this._m1.values(); + } +} diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index 85234d90197..e8cfca7d714 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { LinkedMap, LRUCache, ResourceMap, Touch } from 'vs/base/common/map'; +import { BidirectionalMap, LinkedMap, LRUCache, ResourceMap, Touch } from 'vs/base/common/map'; import { extUriIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -485,3 +485,89 @@ suite('Map', () => { }); }); +suite('BidirectionalMap', () => { + test('should set and get values correctly', () => { + const map = new BidirectionalMap(); + map.set('one', 1); + map.set('two', 2); + map.set('three', 3); + + assert.strictEqual(map.get('one'), 1); + assert.strictEqual(map.get('two'), 2); + assert.strictEqual(map.get('three'), 3); + }); + + test('should get keys by value correctly', () => { + const map = new BidirectionalMap(); + map.set('one', 1); + map.set('two', 2); + map.set('three', 3); + + assert.strictEqual(map.getKey(1), 'one'); + assert.strictEqual(map.getKey(2), 'two'); + assert.strictEqual(map.getKey(3), 'three'); + }); + + test('should delete values correctly', () => { + const map = new BidirectionalMap(); + map.set('one', 1); + map.set('two', 2); + map.set('three', 3); + + assert.strictEqual(map.delete('one'), true); + assert.strictEqual(map.get('one'), undefined); + assert.strictEqual(map.getKey(1), undefined); + + assert.strictEqual(map.delete('two'), true); + assert.strictEqual(map.get('two'), undefined); + assert.strictEqual(map.getKey(2), undefined); + + assert.strictEqual(map.delete('three'), true); + assert.strictEqual(map.get('three'), undefined); + assert.strictEqual(map.getKey(3), undefined); + }); + + test('should handle non-existent keys correctly', () => { + const map = new BidirectionalMap(); + map.set('one', 1); + map.set('two', 2); + map.set('three', 3); + + assert.strictEqual(map.get('four'), undefined); + assert.strictEqual(map.getKey(4), undefined); + assert.strictEqual(map.delete('four'), false); + }); + + test('should handle forEach correctly', () => { + const map = new BidirectionalMap(); + map.set('one', 1); + map.set('two', 2); + map.set('three', 3); + + const keys: string[] = []; + const values: number[] = []; + map.forEach((value, key) => { + keys.push(key); + values.push(value); + }); + + assert.deepStrictEqual(keys, ['one', 'two', 'three']); + assert.deepStrictEqual(values, [1, 2, 3]); + }); + + test('should handle clear correctly', () => { + const map = new BidirectionalMap(); + map.set('one', 1); + map.set('two', 2); + map.set('three', 3); + + map.clear(); + + assert.strictEqual(map.get('one'), undefined); + assert.strictEqual(map.get('two'), undefined); + assert.strictEqual(map.get('three'), undefined); + assert.strictEqual(map.getKey(1), undefined); + assert.strictEqual(map.getKey(2), undefined); + assert.strictEqual(map.getKey(3), undefined); + }); +}); diff --git a/src/vs/editor/common/services/markerDecorationsService.ts b/src/vs/editor/common/services/markerDecorationsService.ts index 935b479a7f8..c6397b71e0a 100644 --- a/src/vs/editor/common/services/markerDecorationsService.ts +++ b/src/vs/editor/common/services/markerDecorationsService.ts @@ -17,46 +17,134 @@ import { IMarkerDecorationsService } from 'vs/editor/common/services/markerDecor import { Schemas } from 'vs/base/common/network'; import { Emitter, Event } from 'vs/base/common/event'; import { minimapWarning, minimapError } from 'vs/platform/theme/common/colorRegistry'; -import { ResourceMap } from 'vs/base/common/map'; +import { BidirectionalMap, ResourceMap } from 'vs/base/common/map'; +import { diffSets } from 'vs/base/common/collections'; +export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService { + + declare readonly _serviceBrand: undefined; + + private readonly _onDidChangeMarker = this._register(new Emitter()); + readonly onDidChangeMarker: Event = this._onDidChangeMarker.event; + + private readonly _markerDecorations = new ResourceMap(); + + constructor( + @IModelService modelService: IModelService, + @IMarkerService private readonly _markerService: IMarkerService + ) { + super(); + modelService.getModels().forEach(model => this._onModelAdded(model)); + this._register(modelService.onModelAdded(this._onModelAdded, this)); + this._register(modelService.onModelRemoved(this._onModelRemoved, this)); + this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this)); + } + + override dispose() { + super.dispose(); + this._markerDecorations.forEach(value => value.dispose()); + this._markerDecorations.clear(); + } + + getMarker(uri: URI, decoration: IModelDecoration): IMarker | null { + const markerDecorations = this._markerDecorations.get(uri); + return markerDecorations ? (markerDecorations.getMarker(decoration) || null) : null; + } + + getLiveMarkers(uri: URI): [Range, IMarker][] { + const markerDecorations = this._markerDecorations.get(uri); + return markerDecorations ? markerDecorations.getMarkers() : []; + } + + private _handleMarkerChange(changedResources: readonly URI[]): void { + changedResources.forEach((resource) => { + const markerDecorations = this._markerDecorations.get(resource); + if (markerDecorations) { + this._updateDecorations(markerDecorations); + } + }); + } + + private _onModelAdded(model: ITextModel): void { + const markerDecorations = new MarkerDecorations(model); + this._markerDecorations.set(model.uri, markerDecorations); + this._updateDecorations(markerDecorations); + } + + private _onModelRemoved(model: ITextModel): void { + const markerDecorations = this._markerDecorations.get(model.uri); + if (markerDecorations) { + markerDecorations.dispose(); + this._markerDecorations.delete(model.uri); + } + + // clean up markers for internal, transient models + if (model.uri.scheme === Schemas.inMemory + || model.uri.scheme === Schemas.internal + || model.uri.scheme === Schemas.vscode) { + this._markerService?.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); + } + } + + private _updateDecorations(markerDecorations: MarkerDecorations): void { + // Limit to the first 500 errors/warnings + const markers = this._markerService.read({ resource: markerDecorations.model.uri, take: 500 }); + if (markerDecorations.update(markers)) { + this._onDidChangeMarker.fire(markerDecorations.model); + } + } +} class MarkerDecorations extends Disposable { - private readonly _markersData: Map = new Map(); + private readonly _map = new BidirectionalMap(); constructor( readonly model: ITextModel ) { super(); this._register(toDisposable(() => { - this.model.deltaDecorations([...this._markersData.keys()], []); - this._markersData.clear(); + this.model.deltaDecorations([...this._map.values()], []); + this._map.clear(); })); } public update(markers: IMarker[]): boolean { - const newDecorations: IModelDeltaDecoration[] = markers.map((marker) => { + + // We use the fact that marker instances are not recreated when different owners + // update. So we can compare references to find out what changed since the last update. + + const { added, removed } = diffSets(new Set(this._map.keys()), new Set(markers)); + + if (added.length === 0 && removed.length === 0) { + return false; + } + + const oldIds: string[] = removed.map(marker => this._map.get(marker)!); + const newDecorations: IModelDeltaDecoration[] = added.map(marker => { return { range: this._createDecorationRange(this.model, marker), options: this._createDecorationOption(marker) }; }); - const oldIds = [...this._markersData.keys()]; - this._markersData.clear(); + const ids = this.model.deltaDecorations(oldIds, newDecorations); - for (let index = 0; index < ids.length; index++) { - this._markersData.set(ids[index], markers[index]); + for (const removedMarker of removed) { + this._map.delete(removedMarker); } - return oldIds.length !== 0 || ids.length !== 0; + for (let index = 0; index < ids.length; index++) { + this._map.set(added[index], ids[index]); + } + return true; } getMarker(decoration: IModelDecoration): IMarker | undefined { - return this._markersData.get(decoration.id); + return this._map.getKey(decoration.id); } getMarkers(): [Range, IMarker][] { const res: [Range, IMarker][] = []; - this._markersData.forEach((marker, id) => { + this._map.forEach((id, marker) => { const range = this.model.getDecorationRange(id); if (range) { res.push([range, marker]); @@ -177,78 +265,3 @@ class MarkerDecorations extends Disposable { return false; } } - -export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService { - - declare readonly _serviceBrand: undefined; - - private readonly _onDidChangeMarker = this._register(new Emitter()); - readonly onDidChangeMarker: Event = this._onDidChangeMarker.event; - - private readonly _markerDecorations = new ResourceMap(); - - constructor( - @IModelService modelService: IModelService, - @IMarkerService private readonly _markerService: IMarkerService - ) { - super(); - modelService.getModels().forEach(model => this._onModelAdded(model)); - this._register(modelService.onModelAdded(this._onModelAdded, this)); - this._register(modelService.onModelRemoved(this._onModelRemoved, this)); - this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this)); - } - - override dispose() { - super.dispose(); - this._markerDecorations.forEach(value => value.dispose()); - this._markerDecorations.clear(); - } - - getMarker(uri: URI, decoration: IModelDecoration): IMarker | null { - const markerDecorations = this._markerDecorations.get(uri); - return markerDecorations ? (markerDecorations.getMarker(decoration) || null) : null; - } - - getLiveMarkers(uri: URI): [Range, IMarker][] { - const markerDecorations = this._markerDecorations.get(uri); - return markerDecorations ? markerDecorations.getMarkers() : []; - } - - private _handleMarkerChange(changedResources: readonly URI[]): void { - changedResources.forEach((resource) => { - const markerDecorations = this._markerDecorations.get(resource); - if (markerDecorations) { - this._updateDecorations(markerDecorations); - } - }); - } - - private _onModelAdded(model: ITextModel): void { - const markerDecorations = new MarkerDecorations(model); - this._markerDecorations.set(model.uri, markerDecorations); - this._updateDecorations(markerDecorations); - } - - private _onModelRemoved(model: ITextModel): void { - const markerDecorations = this._markerDecorations.get(model.uri); - if (markerDecorations) { - markerDecorations.dispose(); - this._markerDecorations.delete(model.uri); - } - - // clean up markers for internal, transient models - if (model.uri.scheme === Schemas.inMemory - || model.uri.scheme === Schemas.internal - || model.uri.scheme === Schemas.vscode) { - this._markerService?.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); - } - } - - private _updateDecorations(markerDecorations: MarkerDecorations): void { - // Limit to the first 500 errors/warnings - const markers = this._markerService.read({ resource: markerDecorations.model.uri, take: 500 }); - if (markerDecorations.update(markers)) { - this._onDidChangeMarker.fire(markerDecorations.model); - } - } -} From c8123491a9a3b985f120f08cdeb9d7646f2a0000 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 5 Sep 2023 10:11:22 -0400 Subject: [PATCH 492/607] clean up, make sure all cases work --- src/vs/editor/browser/config/tabFocus.ts | 13 +++++----- .../editor/browser/widget/codeEditorWidget.ts | 8 +++---- .../browser/parts/editor/editorStatus.ts | 17 ++++++++----- .../browser/parts/editor/tabFocus.ts | 24 ++++++------------- 4 files changed, 28 insertions(+), 34 deletions(-) diff --git a/src/vs/editor/browser/config/tabFocus.ts b/src/vs/editor/browser/config/tabFocus.ts index c42402aa964..f0102e4c0c3 100644 --- a/src/vs/editor/browser/config/tabFocus.ts +++ b/src/vs/editor/browser/config/tabFocus.ts @@ -6,18 +6,17 @@ import { Emitter, Event } from 'vs/base/common/event'; class TabFocusImpl { - private _tabFocusEditor: boolean = false; - - private readonly _onDidChangeTabFocus = new Emitter(); - public readonly onDidChangeTabFocus: Event = this._onDidChangeTabFocus.event; + private _tabFocus: boolean = false; + private readonly _onDidChangeTabFocus = new Emitter(); + public readonly onDidChangeTabFocus: Event = this._onDidChangeTabFocus.event; public getTabFocusMode(): boolean { - return this._tabFocusEditor; + return this._tabFocus; } public setTabFocusMode(tabFocusMode: boolean): void { - this._tabFocusEditor = tabFocusMode; - this._onDidChangeTabFocus.fire(); + this._tabFocus = tabFocusMode; + this._onDidChangeTabFocus.fire(this._tabFocus); } } diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index a4c07939710..dd1275d834c 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -1984,7 +1984,7 @@ class EditorContextKeysManager extends Disposable { private readonly _editorFocus: IContextKey; private readonly _textInputFocus: IContextKey; private readonly _editorTextFocus: IContextKey; - private readonly _editorTabMovesFocus: IContextKey; + private readonly _tabMovesFocus: IContextKey; private readonly _editorReadonly: IContextKey; private readonly _inDiffEditor: IContextKey; private readonly _editorColumnSelection: IContextKey; @@ -2007,7 +2007,7 @@ class EditorContextKeysManager extends Disposable { this._editorFocus = EditorContextKeys.focus.bindTo(contextKeyService); this._textInputFocus = EditorContextKeys.textInputFocus.bindTo(contextKeyService); this._editorTextFocus = EditorContextKeys.editorTextFocus.bindTo(contextKeyService); - this._editorTabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService); + this._tabMovesFocus = EditorContextKeys.tabMovesFocus.bindTo(contextKeyService); this._editorReadonly = EditorContextKeys.readOnly.bindTo(contextKeyService); this._inDiffEditor = EditorContextKeys.inDiffEditor.bindTo(contextKeyService); this._editorColumnSelection = EditorContextKeys.columnSelection.bindTo(contextKeyService); @@ -2024,7 +2024,7 @@ class EditorContextKeysManager extends Disposable { this._register(this._editor.onDidBlurEditorText(() => this._updateFromFocus())); this._register(this._editor.onDidChangeModel(() => this._updateFromModel())); this._register(this._editor.onDidChangeConfiguration(() => this._updateFromModel())); - this._register(TabFocus.onDidChangeTabFocus(() => this._editorTabMovesFocus.set(TabFocus.getTabFocusMode()))); + this._register(TabFocus.onDidChangeTabFocus((tabFocusMode: boolean) => this._tabMovesFocus.set(tabFocusMode))); this._updateFromConfig(); this._updateFromSelection(); @@ -2037,7 +2037,7 @@ class EditorContextKeysManager extends Disposable { private _updateFromConfig(): void { const options = this._editor.getOptions(); - this._editorTabMovesFocus.set(TabFocus.getTabFocusMode()); + this._tabMovesFocus.set(TabFocus.getTabFocusMode()); this._editorReadonly.set(options.get(EditorOption.readOnly)); this._inDiffEditor.set(options.get(EditorOption.inDiffEditor)); this._editorColumnSelection.set(options.get(EditorOption.columnSelection)); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 40de5fbfa2a..a73ebcd2053 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -28,7 +28,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { ILanguageService, ILanguageSelection } from 'vs/editor/common/languages/language'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; -import { TabFocus } from 'vs/editor/browser/config/tabFocus'; import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { EncodingMode, IEncodingSupport, ILanguageSupport, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -310,7 +309,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { @ILanguageService private readonly languageService: ILanguageService, @ITextFileService private readonly textFileService: ITextFileService, @IStatusbarService private readonly statusbarService: IStatusbarService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); this.tabFocusMode = instantiationService.createInstance(TabFocusMode); @@ -322,8 +322,13 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { this._register(this.editorService.onDidActiveEditorChange(() => this.updateStatusBar())); this._register(this.textFileService.untitled.onDidChangeEncoding(model => this.onResourceEncodingChange(model.resource))); this._register(this.textFileService.files.onDidChangeEncoding(model => this.onResourceEncodingChange((model.resource)))); - this._register(Event.runAndSubscribe(TabFocus.onDidChangeTabFocus, () => this.onTabFocusModeChange())); - this._register(this.tabFocusMode.onDidChange(() => this.onTabFocusModeChange())); + this._register(Event.runAndSubscribe(this.tabFocusMode.onDidChange, (tabFocusMode) => { + if (tabFocusMode !== undefined) { + this.onTabFocusModeChange(tabFocusMode); + } else { + this.onTabFocusModeChange(this.configurationService.getValue('editor.tabFocusMode')); + } + })); } private registerCommands(): void { @@ -819,8 +824,8 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution { } } - private onTabFocusModeChange(): void { - const info: StateDelta = { type: 'tabFocusMode', tabFocusMode: TabFocus.getTabFocusMode() }; + private onTabFocusModeChange(tabFocusMode: boolean): void { + const info: StateDelta = { type: 'tabFocusMode', tabFocusMode }; this.updateState(info); } diff --git a/src/vs/workbench/browser/parts/editor/tabFocus.ts b/src/vs/workbench/browser/parts/editor/tabFocus.ts index 66515874450..79ad04a21dd 100644 --- a/src/vs/workbench/browser/parts/editor/tabFocus.ts +++ b/src/vs/workbench/browser/parts/editor/tabFocus.ts @@ -7,33 +7,23 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { TabFocus } from 'vs/editor/browser/config/tabFocus'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; - -export const editorTabFocusContextKey = new RawContextKey('editorTabFocusMode', false, true); export class TabFocusMode extends Disposable { - private readonly _onDidChange = this._register(new Emitter()); + private readonly _onDidChange = this._register(new Emitter()); readonly onDidChange = this._onDidChange.event; - private _editorContext: IContextKey; - constructor( - @IContextKeyService contextKeyService: IContextKeyService, - @IConfigurationService configurationService: IConfigurationService - ) { + constructor(@IConfigurationService configurationService: IConfigurationService) { super(); - - this._editorContext = editorTabFocusContextKey.bindTo(contextKeyService); + TabFocus.onDidChangeTabFocus((tabFocusMode) => this._onDidChange.fire(tabFocusMode)); const editorConfig: boolean = configurationService.getValue('editor.tabFocusMode'); - this._editorContext.set(editorConfig); TabFocus.setTabFocusMode(editorConfig); + this._onDidChange.fire(editorConfig); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('editor.tabFocusMode')) { - const editorConfig: boolean = configurationService.getValue('editor.tabFocusMode'); - TabFocus.setTabFocusMode(editorConfig); - this._editorContext.set(editorConfig); - this._onDidChange.fire(); + const value: boolean = configurationService.getValue('editor.tabFocusMode'); + TabFocus.setTabFocusMode(value); + this._onDidChange.fire(value); } })); - TabFocus.onDidChangeTabFocus(() => this._editorContext.set(TabFocus.getTabFocusMode())); } } From 2312fcc4420960b9d9e1fae5a0b9a4365e8a5606 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 5 Sep 2023 10:15:28 -0400 Subject: [PATCH 493/607] tweak comment --- src/vs/editor/common/config/editorOptions.ts | 2 +- src/vs/monaco.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 1604747fdb9..31fd45a4430 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -726,7 +726,7 @@ export interface IEditorOptions { pasteAs?: IPasteAsOptions; /** - * Controls whether the editor receives tabs or defers them to the workbench for navigation. + * Controls whether the editor / terminal receives tabs or defers them to the workbench for navigation. */ tabFocusMode?: boolean; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 2148581e50f..279a605ba52 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3690,7 +3690,7 @@ declare namespace monaco.editor { */ pasteAs?: IPasteAsOptions; /** - * Controls whether the editor receives tabs or defers them to the workbench for navigation. + * Controls whether the editor / terminal receives tabs or defers them to the workbench for navigation. */ tabFocusMode?: boolean; /** From a3d328a65c06f693ab4c8b9bcd469eaac1c3c522 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 5 Sep 2023 07:19:43 -0700 Subject: [PATCH 494/607] testing: fix most disposable leaks in testing, tree (#192026) This fixes disposable leaks in the testing tests. These tests also encompass tree views. One big source of leaks I found was `Event.chain`: technically, every step in the chain is an IDisposable and needs to be disposed. However, this is very noisy and unergonomic. As an alternative, I introduce a very small `chain2` implementation which only requires disposing the resulting event listener, like an ordinary emitter. It doesn't support debouncing, though it could we if want it to; there was only a single usable of the chained `debounce` method in our codebase. For #190503 --- src/vs/base/browser/ui/list/listView.ts | 4 +- src/vs/base/browser/ui/list/listWidget.ts | 120 ++++++++++-------- src/vs/base/browser/ui/tree/abstractTree.ts | 36 +++--- src/vs/base/common/event.ts | 104 ++++++++++++++- src/vs/base/test/common/event.test.ts | 78 ++++++++++++ .../contrib/testing/common/observableValue.ts | 3 +- .../testing/common/testExplorerFilterState.ts | 15 ++- .../testing/common/testProfileService.ts | 11 +- .../contrib/testing/common/testResult.ts | 14 +- .../testing/common/testResultService.ts | 6 +- .../testing/common/testResultStorage.ts | 8 +- .../hierarchalByLocation.test.ts | 18 ++- .../testing/test/browser/testObjectTree.ts | 2 +- .../common/testExplorerFilterState.test.ts | 14 +- .../test/common/testProfileService.test.ts | 17 ++- .../test/common/testResultService.test.ts | 36 ++++-- .../test/common/testResultStorage.test.ts | 16 ++- .../testing/test/common/testingUri.test.ts | 3 + 18 files changed, 382 insertions(+), 123 deletions(-) diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index b73e5811121..769778f62f7 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -403,11 +403,11 @@ export class ListView implements IListView { this.disposables.add(Gesture.addTarget(this.rowsContainer)); - this.scrollable = new Scrollable({ + this.scrollable = this.disposables.add(new Scrollable({ forceIntegerValues: true, smoothScrollDuration: (options.smoothScrolling ?? false) ? 125 : 0, scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(cb) - }); + })); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { alwaysConsumeMouseWheel: options.alwaysConsumeMouseWheel ?? DefaultOptions.alwaysConsumeMouseWheel, horizontal: ScrollbarVisibility.Auto, diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 262d3ca9e2e..070cc08eeb4 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -293,12 +293,15 @@ class KeyboardController implements IDisposable { private readonly disposables = new DisposableStore(); private readonly multipleSelectionDisposables = new DisposableStore(); + private multipleSelectionSupport: boolean | undefined; @memoize - private get onKeyDown(): Event.IChainableEvent { - return this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event) - .filter(e => !isInputElement(e.target as HTMLElement)) - .map(e => new StandardKeyboardEvent(e))); + private get onKeyDown(): Event { + return Event.chain2( + this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => + $.filter(e => !isInputElement(e.target as HTMLElement)) + .map(e => new StandardKeyboardEvent(e)) + ); } constructor( @@ -306,25 +309,32 @@ class KeyboardController implements IDisposable { private view: IListView, options: IListOptions ) { - this.onKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables); - this.onKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this, this.disposables); - this.onKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.disposables); - this.onKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUpArrow, this, this.disposables); - this.onKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDownArrow, this, this.disposables); - this.onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables); - - if (options.multipleSelectionSupport !== false) { - this.onKeyDown.filter(e => (platform.isMacintosh ? e.metaKey : e.ctrlKey) && e.keyCode === KeyCode.KeyA).on(this.onCtrlA, this, this.multipleSelectionDisposables); - } + this.multipleSelectionSupport = options.multipleSelectionSupport; + this.disposables.add(this.onKeyDown(e => { + switch (e.keyCode) { + case KeyCode.Enter: + return this.onEnter(e); + case KeyCode.UpArrow: + return this.onUpArrow(e); + case KeyCode.DownArrow: + return this.onDownArrow(e); + case KeyCode.PageUp: + return this.onPageUpArrow(e); + case KeyCode.PageDown: + return this.onPageDownArrow(e); + case KeyCode.Escape: + return this.onEscape(e); + case KeyCode.KeyA: + if (this.multipleSelectionSupport && (platform.isMacintosh ? e.metaKey : e.ctrlKey)) { + this.onCtrlA(e); + } + } + })); } updateOptions(optionsUpdate: IListOptionsUpdate): void { if (optionsUpdate.multipleSelectionSupport !== undefined) { - this.multipleSelectionDisposables.clear(); - - if (optionsUpdate.multipleSelectionSupport) { - this.onKeyDown.filter(e => (platform.isMacintosh ? e.metaKey : e.ctrlKey) && e.keyCode === KeyCode.KeyA).on(this.onCtrlA, this, this.multipleSelectionDisposables); - } + this.multipleSelectionSupport = optionsUpdate.multipleSelectionSupport; } } @@ -464,15 +474,15 @@ class TypeNavigationController implements IDisposable { let typing = false; - const onChar = this.enabledDisposables.add(Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)) - .filter(e => !isInputElement(e.target as HTMLElement)) - .filter(() => this.mode === TypeNavigationMode.Automatic || this.triggered) - .map(event => new StandardKeyboardEvent(event)) - .filter(e => typing || this.keyboardNavigationEventFilter(e)) - .filter(e => this.delegate.mightProducePrintableCharacter(e)) - .forEach(e => EventHelper.stop(e, true)) - .map(event => event.browserEvent.key) - .event; + const onChar = Event.chain2(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => + $.filter(e => !isInputElement(e.target as HTMLElement)) + .filter(() => this.mode === TypeNavigationMode.Automatic || this.triggered) + .map(event => new StandardKeyboardEvent(event)) + .filter(e => typing || this.keyboardNavigationEventFilter(e)) + .filter(e => this.delegate.mightProducePrintableCharacter(e)) + .forEach(e => EventHelper.stop(e, true)) + .map(event => event.browserEvent.key) + ); const onClear = Event.debounce(onChar, () => null, 800, undefined, undefined, undefined, this.enabledDisposables); const onInput = Event.reduce(Event.any(onChar, onClear), (r, i) => i === null ? null : ((r || '') + i), undefined, this.enabledDisposables); @@ -570,12 +580,14 @@ class DOMFocusController implements IDisposable { private list: List, private view: IListView ) { - const onKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event)) + const onKeyDown = Event.chain2(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event, $ => $ .filter(e => !isInputElement(e.target as HTMLElement)) - .map(e => new StandardKeyboardEvent(e)); + .map(e => new StandardKeyboardEvent(e)) + ); - onKeyDown.filter(e => e.keyCode === KeyCode.Tab && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey) - .on(this.onTab, this, this.disposables); + const onTab = Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Tab && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey)); + + onTab(this.onTab, this, this.disposables); } private onTab(e: StandardKeyboardEvent): void { @@ -1348,31 +1360,29 @@ export class List implements ISpliceable, IDisposable { @memoize get onContextMenu(): Event> { let didJustPressContextMenuKey = false; - const fromKeyDown = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event)) - .map(e => new StandardKeyboardEvent(e)) - .filter(e => didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) - .map(e => EventHelper.stop(e, true)) - .filter(() => false) - .event as Event; + const fromKeyDown: Event = Event.chain2(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => + $.map(e => new StandardKeyboardEvent(e)) + .filter(e => didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) + .map(e => EventHelper.stop(e, true)) + .filter(() => false)); - const fromKeyUp = this.disposables.add(Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event)) - .forEach(() => didJustPressContextMenuKey = false) - .map(e => new StandardKeyboardEvent(e)) - .filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) - .map(e => EventHelper.stop(e, true)) - .map(({ browserEvent }) => { - const focus = this.getFocus(); - const index = focus.length ? focus[0] : undefined; - const element = typeof index !== 'undefined' ? this.view.element(index) : undefined; - const anchor = typeof index !== 'undefined' ? this.view.domElement(index) as HTMLElement : this.view.domNode; - return { index, element, anchor, browserEvent }; - }) - .event; + const fromKeyUp = Event.chain2(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event, $ => + $.forEach(() => didJustPressContextMenuKey = false) + .map(e => new StandardKeyboardEvent(e)) + .filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) + .map(e => EventHelper.stop(e, true)) + .map(({ browserEvent }) => { + const focus = this.getFocus(); + const index = focus.length ? focus[0] : undefined; + const element = typeof index !== 'undefined' ? this.view.element(index) : undefined; + const anchor = typeof index !== 'undefined' ? this.view.domElement(index) as HTMLElement : this.view.domNode; + return { index, element, anchor, browserEvent }; + })); - const fromMouse = this.disposables.add(Event.chain(this.view.onContextMenu)) - .filter(_ => !didJustPressContextMenuKey) - .map(({ element, index, browserEvent }) => ({ element, index, anchor: new StandardMouseEvent(browserEvent), browserEvent })) - .event; + const fromMouse = Event.chain2(this.view.onContextMenu, $ => + $.filter(_ => !didJustPressContextMenuKey) + .map(({ element, index, browserEvent }) => ({ element, index, anchor: new StandardMouseEvent(browserEvent), browserEvent })) + ); return Event.any>(fromKeyDown, fromKeyUp, fromMouse); } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 70199a8164c..4327f53551f 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -19,7 +19,7 @@ import { getVisibleState, isFilterResult } from 'vs/base/browser/ui/tree/indexTr import { ICollapseStateChangeEvent, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeEvent, ITreeFilter, ITreeModel, ITreeModelSpliceEvent, ITreeMouseEvent, ITreeNavigator, ITreeNode, ITreeRenderer, TreeDragOverBubble, TreeError, TreeFilterResult, TreeMouseEventTarget, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { Action } from 'vs/base/common/actions'; import { distinct, equals, firstOrDefault, range } from 'vs/base/common/arrays'; -import { disposableTimeout, timeout } from 'vs/base/common/async'; +import { Delayer, disposableTimeout, timeout } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { SetMap } from 'vs/base/common/collections'; @@ -814,9 +814,7 @@ class FindWidget extends Disposable { this.mode = mode; const emitter = this._register(new DomEmitter(this.findInput.inputBox.inputElement, 'keydown')); - const onKeyDown = this._register(Event.chain(emitter.event)) - .map(e => new StandardKeyboardEvent(e)) - .event; + const onKeyDown = Event.chain2(emitter.event, $ => $.map(e => new StandardKeyboardEvent(e))); this._register(onKeyDown((e): any => { // Using equals() so we reserve modified keys for future use @@ -886,9 +884,7 @@ class FindWidget extends Disposable { })); })); - const onGrabKeyDown = this._register(Event.chain(this._register(new DomEmitter(this.elements.grab, 'keydown')).event)) - .map(e => new StandardKeyboardEvent(e)) - .event; + const onGrabKeyDown = Event.chain2(this._register(new DomEmitter(this.elements.grab, 'keydown')).event, $ => $.map(e => new StandardKeyboardEvent(e))); this._register(onGrabKeyDown((e): any => { let right: number | undefined; @@ -1624,9 +1620,10 @@ export abstract class AbstractTree implements IDisposable // We debounce it with 0 delay since these events may fire in the same stack and we only // want to run this once. It also doesn't matter if it runs on the next tick since it's only // a nice to have UI feature. - onDidChangeActiveNodes.input = Event.chain(Event.any(onDidModelSplice, this.focus.onDidChange, this.selection.onDidChange)) - .debounce(() => null, 0) - .map(() => { + const activeNodesEmitter = this.disposables.add(new Emitter[]>()); + const activeNodesDebounce = this.disposables.add(new Delayer(0)); + this.disposables.add(Event.any(onDidModelSplice, this.focus.onDidChange, this.selection.onDidChange)(() => { + activeNodesDebounce.trigger(() => { const set = new Set>(); for (const node of this.focus.getNodes()) { @@ -1637,17 +1634,20 @@ export abstract class AbstractTree implements IDisposable set.add(node); } - return [...set.values()]; - }).event; + activeNodesEmitter.fire([...set.values()]); + }); + })); + onDidChangeActiveNodes.input = activeNodesEmitter.event; if (_options.keyboardSupport !== false) { - const onKeyDown = Event.chain(this.view.onKeyDown) - .filter(e => !isInputElement(e.target as HTMLElement)) - .map(e => new StandardKeyboardEvent(e)); + const onKeyDown = Event.chain2(this.view.onKeyDown, $ => + $.filter(e => !isInputElement(e.target as HTMLElement)) + .map(e => new StandardKeyboardEvent(e)) + ); - onKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow).on(this.onLeftArrow, this, this.disposables); - onKeyDown.filter(e => e.keyCode === KeyCode.RightArrow).on(this.onRightArrow, this, this.disposables); - onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); + Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.LeftArrow))(this.onLeftArrow, this, this.disposables); + Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.RightArrow))(this.onRightArrow, this, this.disposables); + Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Space))(this.onSpace, this, this.disposables); } if ((_options.findWidgetEnabled ?? true) && _options.keyboardNavigationLabelProvider && _options.contextViewProvider) { diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index c26c0271f39..7f3b4406d7a 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -165,7 +165,10 @@ export namespace Event { export function any(...events: Event[]): Event; export function any(...events: Event[]): Event; export function any(...events: Event[]): Event { - return (listener, thisArgs = null, disposables?) => combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e), null, disposables))); + return (listener, thisArgs = null, disposables?) => { + const disposable = combinedDisposable(...events.map(event => event(e => listener.call(thisArgs, e)))); + return addAndReturnDisposable(disposable, disposables); + }; } /** @@ -205,6 +208,19 @@ export namespace Event { return emitter.event; } + /** + * Adds the IDisposable to the store if it's set, and returns it. Useful to + * Event function implementation. + */ + function addAndReturnDisposable(d: T, store: DisposableStore | IDisposable[] | undefined): T { + if (store instanceof Array) { + store.push(d); + } else if (store) { + store.add(d); + } + return d; + } + /** * Given an event, creates a new emitter that event that will debounce events based on {@link delay} and give an * array event object of all events that fired. @@ -422,6 +438,92 @@ export namespace Event { return emitter.event; } + /** + * Implements event chaining, in a way that avoids having disposable + * intermediates at each step like {@link chain} does. + */ + export function chain2(event: Event, sythensize: ($: IChainableSythensis) => IChainableSythensis): Event { + const fn: Event = (listener, thisArgs, disposables) => { + const cs = new ChainableSynthesis(); + sythensize(cs); + return event(value => { + const result = cs.evaluate(value); + if (result !== HaltChainable) { + listener(result); + } + }, thisArgs, disposables); + }; + + return fn; + } + + const HaltChainable = Symbol('HaltChainable'); + + class ChainableSynthesis implements IChainableSythensis { + private readonly steps: ((input: any) => any)[] = []; + + map(fn: (i: any) => O): this { + this.steps.push(fn); + return this; + } + + forEach(fn: (i: any) => void): this { + this.steps.push(v => { + fn(v); + return v; + }); + return this; + } + + filter(fn: (e: any) => boolean): this { + this.steps.push(v => fn(v) ? v : HaltChainable); + return this; + } + + reduce(merge: (last: R | undefined, event: any) => R, initial?: R | undefined): this { + let last = initial; + this.steps.push(v => { + last = merge(last, v); + return last; + }); + return this; + } + + latch(equals: (a: any, b: any) => boolean = (a, b) => a === b): ChainableSynthesis { + let firstCall = true; + let cache: any; + this.steps.push(value => { + const shouldEmit = firstCall || !equals(value, cache); + firstCall = false; + cache = value; + return shouldEmit ? value : HaltChainable; + }); + + return this; + } + + public evaluate(value: any) { + for (const step of this.steps) { + value = step(value); + if (value === HaltChainable) { + break; + } + } + + return value; + } + } + + export interface IChainableSythensis { + map(fn: (i: T) => O): IChainableSythensis; + forEach(fn: (i: T) => void): IChainableSythensis; + filter(fn: (e: T) => boolean): IChainableSythensis; + filter(fn: (e: T | R) => e is R): IChainableSythensis; + reduce(merge: (last: R, event: T) => R, initial: R): IChainableSythensis; + reduce(merge: (last: R | undefined, event: T) => R): IChainableSythensis; + latch(equals?: (a: T, b: T) => boolean): IChainableSythensis; + } + export interface IChainableEvent extends IDisposable { event: Event; diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index 30320370f2a..c2ef872140e 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -1507,4 +1507,82 @@ suite('Event utils', () => { assert.deepStrictEqual(calls, [1]); }); }); + + suite('chain2', () => { + let store: DisposableStore; + let em: Emitter; + let calls: number[]; + + teardown(() => { + store.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + + setup(() => { + store = new DisposableStore(); + em = new Emitter(); + store.add(em); + calls = []; + }); + + test('maps', () => { + const ev = Event.chain2(em.event, $ => $.map(v => v * 2)); + store.add(ev(v => calls.push(v))); + em.fire(1); + em.fire(2); + em.fire(3); + assert.deepStrictEqual(calls, [2, 4, 6]); + }); + + test('filters', () => { + const ev = Event.chain2(em.event, $ => $.filter(v => v % 2 === 0)); + store.add(ev(v => calls.push(v))); + em.fire(1); + em.fire(2); + em.fire(3); + em.fire(4); + assert.deepStrictEqual(calls, [2, 4]); + }); + + test('reduces', () => { + const ev = Event.chain2(em.event, $ => $.reduce((acc, v) => acc + v, 0)); + store.add(ev(v => calls.push(v))); + em.fire(1); + em.fire(2); + em.fire(3); + em.fire(4); + assert.deepStrictEqual(calls, [1, 3, 6, 10]); + }); + + test('latches', () => { + const ev = Event.chain2(em.event, $ => $.latch()); + store.add(ev(v => calls.push(v))); + em.fire(1); + em.fire(1); + em.fire(2); + em.fire(2); + em.fire(3); + em.fire(3); + em.fire(1); + assert.deepStrictEqual(calls, [1, 2, 3, 1]); + }); + + test('does everything', () => { + const ev = Event.chain2(em.event, $ => $ + .filter(v => v % 2 === 0) + .map(v => v * 2) + .reduce((acc, v) => acc + v, 0) + .latch() + ); + + store.add(ev(v => calls.push(v))); + em.fire(1); + em.fire(2); + em.fire(3); + em.fire(4); + em.fire(0); + assert.deepStrictEqual(calls, [4, 12]); + }); + }); }); diff --git a/src/vs/workbench/contrib/testing/common/observableValue.ts b/src/vs/workbench/contrib/testing/common/observableValue.ts index bc14ca7fb0f..942a75307e5 100644 --- a/src/vs/workbench/contrib/testing/common/observableValue.ts +++ b/src/vs/workbench/contrib/testing/common/observableValue.ts @@ -35,7 +35,8 @@ export class MutableObservableValue extends Disposable implements IObservable public static stored(stored: StoredValue, defaultValue: T) { const o = new MutableObservableValue(stored.get(defaultValue)); - o.onDidChange(value => stored.store(value)); + o._register(stored); + o._register(o.onDidChange(value => stored.store(value))); return o; } diff --git a/src/vs/workbench/contrib/testing/common/testExplorerFilterState.ts b/src/vs/workbench/contrib/testing/common/testExplorerFilterState.ts index e4f27947199..a92eb4ae7a4 100644 --- a/src/vs/workbench/contrib/testing/common/testExplorerFilterState.ts +++ b/src/vs/workbench/contrib/testing/common/testExplorerFilterState.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from 'vs/base/common/event'; import { splitGlobAware } from 'vs/base/common/glob'; +import { Disposable } from 'vs/base/common/lifecycle'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IObservableValue, MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; @@ -68,7 +69,7 @@ export const ITestExplorerFilterState = createDecorator str.replace(/\s\s+/g, ' ').trim(); -export class TestExplorerFilterState implements ITestExplorerFilterState { +export class TestExplorerFilterState extends Disposable implements ITestExplorerFilterState { declare _serviceBrand: undefined; private readonly focusEmitter = new Emitter(); /** @@ -86,20 +87,22 @@ export class TestExplorerFilterState implements ITestExplorerFilterState { public excludeTags = new Set(); /** @inheritdoc */ - public readonly text = new MutableObservableValue(''); + public readonly text = this._register(new MutableObservableValue('')); /** @inheritdoc */ - public readonly fuzzy = MutableObservableValue.stored(new StoredValue({ + public readonly fuzzy = this._register(MutableObservableValue.stored(new StoredValue({ key: 'testHistoryFuzzy', scope: StorageScope.PROFILE, target: StorageTarget.USER, - }, this.storageService), false); + }, this.storageService), false)); - public readonly reveal = new MutableObservableValue(undefined); + public readonly reveal = this._register(new MutableObservableValue(undefined)); public readonly onDidRequestInputFocus = this.focusEmitter.event; - constructor(@IStorageService private readonly storageService: IStorageService) { } + constructor(@IStorageService private readonly storageService: IStorageService) { + super(); + } /** @inheritdoc */ public focusInput() { diff --git a/src/vs/workbench/contrib/testing/common/testProfileService.ts b/src/vs/workbench/contrib/testing/common/testProfileService.ts index 03541ed9053..4c0d99bdf8c 100644 --- a/src/vs/workbench/contrib/testing/common/testProfileService.ts +++ b/src/vs/workbench/contrib/testing/common/testProfileService.ts @@ -13,6 +13,7 @@ import { InternalTestItem, ITestRunProfile, TestRunProfileBitset, testRunProfile import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { IMainThreadTestController } from 'vs/workbench/contrib/testing/common/testService'; +import { Disposable } from 'vs/base/common/lifecycle'; export const ITestProfileService = createDecorator('testProfileService'); @@ -100,11 +101,11 @@ export const capabilityContextKeys = (capabilities: number): [key: string, value [TestingContextKeys.hasCoverableTests.key, (capabilities & TestRunProfileBitset.Coverage) !== 0], ]; -export class TestProfileService implements ITestProfileService { +export class TestProfileService extends Disposable implements ITestProfileService { declare readonly _serviceBrand: undefined; private readonly preferredDefaults: StoredValue<{ [K in TestRunProfileBitset]?: { controllerId: string; profileId: number }[] }>; private readonly capabilitiesContexts: { [K in TestRunProfileBitset]: IContextKey }; - private readonly changeEmitter = new Emitter(); + private readonly changeEmitter = this._register(new Emitter()); private readonly controllerProfiles = new Map(); - private readonly newTaskEmitter = new Emitter(); - private readonly endTaskEmitter = new Emitter(); - private readonly changeEmitter = new Emitter(); +export class LiveTestResult extends Disposable implements ITestResult { + private readonly completeEmitter = this._register(new Emitter()); + private readonly newTaskEmitter = this._register(new Emitter()); + private readonly endTaskEmitter = this._register(new Emitter()); + private readonly changeEmitter = this._register(new Emitter()); /** todo@connor4312: convert to a WellDefinedPrefixTree */ private readonly testById = new Map(); private testMarkerCounter = 0; @@ -334,6 +335,7 @@ export class LiveTestResult implements ITestResult { public readonly persist: boolean, public readonly request: ResolvedTestRunRequest, ) { + super(); } /** @@ -382,7 +384,7 @@ export class LiveTestResult implements ITestResult { * Adds a new run task to the results. */ public addTask(task: ITestRunTask) { - this.tasks.push({ ...task, coverage: new MutableObservableValue(undefined), otherMessages: [], output: new TaskRawOutput() }); + this.tasks.push({ ...task, coverage: this._register(new MutableObservableValue(undefined)), otherMessages: [], output: new TaskRawOutput() }); for (const test of this.tests) { test.tasks.push({ duration: undefined, messages: [], state: TestResultState.Unset }); diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index 69c740dce7c..e931cef40ac 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -7,6 +7,7 @@ import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -179,8 +180,9 @@ export class TestResultService implements ITestResultService { } if (result instanceof LiveTestResult) { - result.onComplete(() => this.onComplete(result)); - result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter); + const ds = new DisposableStore(); + ds.add(result.onComplete(() => this.onComplete(result))); + ds.add(result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter)); this.isRunning.set(true); this.changeResultEmitter.fire({ started: result }); } else { diff --git a/src/vs/workbench/contrib/testing/common/testResultStorage.ts b/src/vs/workbench/contrib/testing/common/testResultStorage.ts index 2b2e027b65b..5574e744b8a 100644 --- a/src/vs/workbench/contrib/testing/common/testResultStorage.ts +++ b/src/vs/workbench/contrib/testing/common/testResultStorage.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { bufferToStream, newWriteableBufferStream, VSBuffer, VSBufferReadableStream, VSBufferWriteableStream } from 'vs/base/common/buffer'; +import { Disposable } from 'vs/base/common/lifecycle'; import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -44,19 +45,20 @@ export const ITestResultStorage = createDecorator('ITestResultStorage'); */ const currentRevision = 1; -export abstract class BaseTestResultStorage implements ITestResultStorage { +export abstract class BaseTestResultStorage extends Disposable implements ITestResultStorage { declare readonly _serviceBrand: undefined; - protected readonly stored = new StoredValue>({ + protected readonly stored = this._register(new StoredValue>({ key: 'storedTestResults', scope: StorageScope.WORKSPACE, target: StorageTarget.MACHINE - }, this.storageService); + }, this.storageService)); constructor( @IStorageService private readonly storageService: IStorageService, @ILogService private readonly logService: ILogService, ) { + super(); } /** diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts index 574835878e2..97b6ef85520 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByLocation.test.ts @@ -5,6 +5,8 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TreeProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/treeProjection'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; @@ -19,9 +21,17 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => { let harness: TestTreeTestHarness; let onTestChanged: Emitter; let resultsService: any; + let ds: DisposableStore; + + teardown(() => { + ds.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); setup(() => { - onTestChanged = new Emitter(); + ds = new DisposableStore(); + onTestChanged = ds.add(new Emitter()); resultsService = { results: [], onResultsChanged: () => undefined, @@ -29,11 +39,7 @@ suite('Workbench - Testing Explorer Hierarchal by Location Projection', () => { getStateById: () => ({ state: { state: 0 }, computedState: 0 }), }; - harness = new TestTreeTestHarness(l => new TestHierarchicalByLocationProjection({}, l, resultsService as any)); - }); - - teardown(() => { - harness.dispose(); + harness = ds.add(new TestTreeTestHarness(l => new TestHierarchicalByLocationProjection({}, l, resultsService as any))); }); test('renders initial tree', async () => { diff --git a/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts b/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts index c1792e29343..1e447daa7d5 100644 --- a/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts +++ b/src/vs/workbench/contrib/testing/test/browser/testObjectTree.ts @@ -104,7 +104,7 @@ export class TestTreeTestHarness T, public readonly c = testStubs.nested()) { super(); this._register(c); - this.c.onDidGenerateDiff(d => this.c.setDiff(d /* don't clear during testing */)); + this._register(this.c.onDidGenerateDiff(d => this.c.setDiff(d /* don't clear during testing */))); const collection = new MainThreadTestCollection((testId, levels) => { this.c.expand(testId, levels); diff --git a/src/vs/workbench/contrib/testing/test/common/testExplorerFilterState.test.ts b/src/vs/workbench/contrib/testing/test/common/testExplorerFilterState.test.ts index cc89323ebdd..2be30fb7d2a 100644 --- a/src/vs/workbench/contrib/testing/test/common/testExplorerFilterState.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testExplorerFilterState.test.ts @@ -4,14 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { InMemoryStorageService } from 'vs/platform/storage/common/storage'; import { TestExplorerFilterState, TestFilterTerm } from 'vs/workbench/contrib/testing/common/testExplorerFilterState'; - suite('TestExplorerFilterState', () => { let t: TestExplorerFilterState; + let ds: DisposableStore; + + teardown(() => { + ds.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + setup(() => { - t = new TestExplorerFilterState(new InMemoryStorageService()); + ds = new DisposableStore(); + t = ds.add(new TestExplorerFilterState(ds.add(new InMemoryStorageService()))); }); const assertFilteringFor = (expected: { [T in TestFilterTerm]?: boolean }) => { diff --git a/src/vs/workbench/contrib/testing/test/common/testProfileService.test.ts b/src/vs/workbench/contrib/testing/test/common/testProfileService.test.ts index 129ce196195..bbb315cfa2f 100644 --- a/src/vs/workbench/contrib/testing/test/common/testProfileService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testProfileService.test.ts @@ -6,6 +6,8 @@ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { TestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; import { ITestRunProfile, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; @@ -13,13 +15,22 @@ import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServic suite('Workbench - TestProfileService', () => { let t: TestProfileService; + let ds: DisposableStore; let idCounter = 0; + + teardown(() => { + ds.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + setup(() => { idCounter = 0; - t = new TestProfileService( + ds = new DisposableStore(); + t = ds.add(new TestProfileService( new MockContextKeyService(), - new TestStorageService(), - ); + ds.add(new TestStorageService()), + )); }); const addProfile = (profile: Partial) => { diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts index 4f19df8e1a4..464cceb9478 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -6,16 +6,17 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { NullLogService } from 'vs/platform/log/common/log'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; -import { HydratedTestResult, LiveTestResult, resultItemParents, TaskRawOutput, TestResultItemChange, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; +import { HydratedTestResult, LiveTestResult, TaskRawOutput, TestResultItemChange, TestResultItemChangeReason, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; import { TestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; -import { InMemoryResultStorage, ITestResultStorage } from 'vs/workbench/contrib/testing/common/testResultStorage'; +import { ITestResultStorage, InMemoryResultStorage } from 'vs/workbench/contrib/testing/common/testResultStorage'; import { ITestTaskState, ResolvedTestRunRequest, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; import { makeEmptyCounts } from 'vs/workbench/contrib/testing/common/testingStates'; -import { getInitializedMainTestCollection, testStubs, TestTestCollection } from 'vs/workbench/contrib/testing/test/common/testStubs'; +import { TestTestCollection, getInitializedMainTestCollection, testStubs } from 'vs/workbench/contrib/testing/test/common/testStubs'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; suite('Workbench - Test Results Service', () => { @@ -26,6 +27,7 @@ suite('Workbench - Test Results Service', () => { let r: TestLiveTestResult; let changed = new Set(); let tests: TestTestCollection; + let ds: DisposableStore; const defaultOpts = (testIds: string[]): ResolvedTestRunRequest => ({ targets: [{ @@ -37,23 +39,33 @@ suite('Workbench - Test Results Service', () => { }); class TestLiveTestResult extends LiveTestResult { + constructor( + id: string, + persist: boolean, + request: ResolvedTestRunRequest, + ) { + super(id, persist, request); + ds.add(this); + } + public setAllToStatePublic(state: TestResultState, taskId: string, when: (task: ITestTaskState, item: TestResultItem) => boolean) { this.setAllToState(state, taskId, when); } } setup(async () => { + ds = new DisposableStore(); changed = new Set(); - r = new TestLiveTestResult( + r = ds.add(new TestLiveTestResult( 'foo', true, defaultOpts(['id-a']), - ); + )); r.onChange(e => changed.add(e)); r.addTask({ id: 't', name: undefined, running: true }); - tests = testStubs.nested(); + tests = ds.add(testStubs.nested()); const ok = await Promise.race([ Promise.resolve(tests.expand(tests.root.id, Infinity)).then(() => true), timeout(1000).then(() => false), @@ -76,6 +88,12 @@ suite('Workbench - Test Results Service', () => { ]); }); + teardown(() => { + ds.dispose(); + }); + + // ensureNoDisposablesAreLeakedInTestSuite(); todo@connor4312 + suite('LiveTestResult', () => { test('is empty if no tests are yet present', async () => { assert.deepStrictEqual(getLabelsIn(new TestLiveTestResult( @@ -190,8 +208,8 @@ suite('Workbench - Test Results Service', () => { } setup(() => { - storage = new InMemoryResultStorage(new TestStorageService(), new NullLogService()); - results = new TestTestResultService(new MockContextKeyService(), storage, new TestProfileService(new MockContextKeyService(), new TestStorageService())); + storage = ds.add(new InMemoryResultStorage(ds.add(new TestStorageService()), new NullLogService())); + results = new TestTestResultService(new MockContextKeyService(), storage, ds.add(new TestProfileService(new MockContextKeyService(), ds.add(new TestStorageService())))); }); test('pushes new result', () => { @@ -208,7 +226,7 @@ suite('Workbench - Test Results Service', () => { results = new TestResultService( new MockContextKeyService(), storage, - new TestProfileService(new MockContextKeyService(), new TestStorageService()), + ds.add(new TestProfileService(new MockContextKeyService(), ds.add(new TestStorageService()))), ); assert.strictEqual(0, results.results.length); diff --git a/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts index a46e0e630a6..98d828c0a3d 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultStorage.test.ts @@ -5,6 +5,8 @@ import * as assert from 'assert'; import { range } from 'vs/base/common/arrays'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITestResult, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult'; import { InMemoryResultStorage, RETAIN_MAX_RESULTS } from 'vs/workbench/contrib/testing/common/testResultStorage'; @@ -13,16 +15,17 @@ import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServic suite('Workbench - Test Result Storage', () => { let storage: InMemoryResultStorage; + let ds: DisposableStore; const makeResult = (taskName = 't') => { - const t = new LiveTestResult( + const t = ds.add(new LiveTestResult( '', true, { targets: [] } - ); + )); t.addTask({ id: taskName, name: undefined, running: true }); - const tests = testStubs.nested(); + const tests = ds.add(testStubs.nested()); tests.expand(tests.root.id, Infinity); t.addTestChainToRun('ctrlId', [ tests.root.toTestItem(), @@ -38,9 +41,14 @@ suite('Workbench - Test Result Storage', () => { assert.deepStrictEqual((await storage.read()).map(r => r.id), stored.map(s => s.id)); setup(async () => { - storage = new InMemoryResultStorage(new TestStorageService(), new NullLogService()); + ds = new DisposableStore(); + storage = ds.add(new InMemoryResultStorage(ds.add(new TestStorageService()), new NullLogService())); }); + teardown(() => ds.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('stores a single result', async () => { const r = range(5).map(() => makeResult()); await storage.persist(r); diff --git a/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts b/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts index b2e1bdf7112..6dd030f9b53 100644 --- a/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testingUri.test.ts @@ -4,9 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { buildTestUri, ParsedTestUri, parseTestUri, TestUriType } from 'vs/workbench/contrib/testing/common/testingUri'; suite('Workbench - Testing URIs', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('round trip', () => { const uris: ParsedTestUri[] = [ { type: TestUriType.ResultActualOutput, taskIndex: 1, messageIndex: 42, resultId: 'r', testExtId: 't' }, From cc7c052e7ce2612d10c51c04f144940fcdda5e58 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 07:38:31 -0700 Subject: [PATCH 495/607] RequestStore test leaks Part of #190503 --- src/vs/platform/terminal/common/requestStore.ts | 5 +++++ .../terminal/test/common/requestStore.test.ts | 13 ++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/terminal/common/requestStore.ts b/src/vs/platform/terminal/common/requestStore.ts index 8e843553d68..62ad1c641e8 100644 --- a/src/vs/platform/terminal/common/requestStore.ts +++ b/src/vs/platform/terminal/common/requestStore.ts @@ -32,6 +32,11 @@ export class RequestStore extends Disposable { ) { super(); this._timeout = timeout === undefined ? 15000 : timeout; + this._register(toDisposable(() => { + for (const d of this._pendingRequestDisposables.values()) { + dispose(d); + } + })); } /** diff --git a/src/vs/platform/terminal/test/common/requestStore.test.ts b/src/vs/platform/terminal/test/common/requestStore.test.ts index 377e878f955..3da52600d94 100644 --- a/src/vs/platform/terminal/test/common/requestStore.test.ts +++ b/src/vs/platform/terminal/test/common/requestStore.test.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { fail, strictEqual } from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ConsoleLogger, ILogService } from 'vs/platform/log/common/log'; import { LogService } from 'vs/platform/log/common/logService'; @@ -11,20 +13,25 @@ import { RequestStore } from 'vs/platform/terminal/common/requestStore'; suite('RequestStore', () => { let instantiationService: TestInstantiationService; + let disposables: DisposableStore; setup(() => { + disposables = new DisposableStore(); instantiationService = new TestInstantiationService(); instantiationService.stub(ILogService, new LogService(new ConsoleLogger())); }); teardown(() => { instantiationService.dispose(); + disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('should resolve requests', async () => { - const store: RequestStore<{ data: string }, { arg: string }> = instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, undefined); + const store: RequestStore<{ data: string }, { arg: string }> = disposables.add(instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, undefined)); let eventArgs: { requestId: number; arg: string } | undefined; - store.onCreateRequest(e => eventArgs = e); + disposables.add(store.onCreateRequest(e => eventArgs = e)); const request = store.createRequest({ arg: 'foo' }); strictEqual(typeof eventArgs?.requestId, 'number'); strictEqual(eventArgs?.arg, 'foo'); @@ -34,7 +41,7 @@ suite('RequestStore', () => { }); test('should reject the promise when the request times out', async () => { - const store: RequestStore<{ data: string }, { arg: string }> = instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, 1); + const store: RequestStore<{ data: string }, { arg: string }> = disposables.add(instantiationService.createInstance(RequestStore<{ data: string }, { arg: string }>, 1)); const request = store.createRequest({ arg: 'foo' }); let threw = false; try { From b960702d037632da7e089ca871c21895ce1ee205 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 07:43:43 -0700 Subject: [PATCH 496/607] Some easy leak files --- src/vs/base/test/common/date.test.ts | 3 +++ src/vs/base/test/common/processes.test.ts | 3 +++ .../platform/terminal/test/common/terminalEnvironment.test.ts | 3 +++ src/vs/platform/terminal/test/common/terminalProfiles.test.ts | 3 +++ src/vs/platform/terminal/test/common/terminalRecorder.test.ts | 3 +++ 5 files changed, 15 insertions(+) diff --git a/src/vs/base/test/common/date.test.ts b/src/vs/base/test/common/date.test.ts index 5826f6d7e55..331c7f0e001 100644 --- a/src/vs/base/test/common/date.test.ts +++ b/src/vs/base/test/common/date.test.ts @@ -5,8 +5,11 @@ import { strictEqual } from 'assert'; import { fromNow } from 'vs/base/common/date'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Date', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('fromNow', () => { test('appendAgoLabel', () => { strictEqual(fromNow(Date.now() - 35000), '35 secs'); diff --git a/src/vs/base/test/common/processes.test.ts b/src/vs/base/test/common/processes.test.ts index 12ade1a1458..d575590ab10 100644 --- a/src/vs/base/test/common/processes.test.ts +++ b/src/vs/base/test/common/processes.test.ts @@ -5,8 +5,11 @@ import * as assert from 'assert'; import * as processes from 'vs/base/common/processes'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Processes', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('sanitizeProcessEnvironment', () => { const env = { FOO: 'bar', diff --git a/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts index 5f8aaca7e15..a6e8eb4f95a 100644 --- a/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts +++ b/src/vs/platform/terminal/test/common/terminalEnvironment.test.ts @@ -5,9 +5,12 @@ import { strictEqual } from 'assert'; import { OperatingSystem, OS } from 'vs/base/common/platform'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { collapseTildePath, sanitizeCwd } from 'vs/platform/terminal/common/terminalEnvironment'; suite('terminalEnvironment', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('collapseTildePath', () => { test('should return empty string for a falsy path', () => { strictEqual(collapseTildePath('', '/foo', '/'), ''); diff --git a/src/vs/platform/terminal/test/common/terminalProfiles.test.ts b/src/vs/platform/terminal/test/common/terminalProfiles.test.ts index 4720794b4f8..789fe9821bb 100644 --- a/src/vs/platform/terminal/test/common/terminalProfiles.test.ts +++ b/src/vs/platform/terminal/test/common/terminalProfiles.test.ts @@ -5,10 +5,13 @@ import { deepStrictEqual } from 'assert'; import { Codicon } from 'vs/base/common/codicons'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ITerminalProfile } from 'vs/platform/terminal/common/terminal'; import { createProfileSchemaEnums } from 'vs/platform/terminal/common/terminalProfiles'; suite('terminalProfiles', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('createProfileSchemaEnums', () => { test('should return an empty array when there are no profiles', () => { deepStrictEqual(createProfileSchemaEnums([]), { diff --git a/src/vs/platform/terminal/test/common/terminalRecorder.test.ts b/src/vs/platform/terminal/test/common/terminalRecorder.test.ts index 9670c17e8c3..b1c523a2228 100644 --- a/src/vs/platform/terminal/test/common/terminalRecorder.test.ts +++ b/src/vs/platform/terminal/test/common/terminalRecorder.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ReplayEntry } from 'vs/platform/terminal/common/terminalProcess'; import { TerminalRecorder } from 'vs/platform/terminal/common/terminalRecorder'; @@ -15,6 +16,8 @@ async function eventsEqual(recorder: TerminalRecorder, expected: ReplayEntry[]) } suite('TerminalRecorder', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('should record dimensions', async () => { const recorder = new TerminalRecorder(1, 2); await eventsEqual(recorder, [ From d52c8b0f33aa390ab58c6329512fa1c01ec39229 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 5 Sep 2023 16:46:40 +0200 Subject: [PATCH 497/607] chore - avoid duplicate imports --- src/vs/base/browser/ui/grid/grid.ts | 3 +- .../minimap/minimapCharRendererFactory.ts | 3 +- .../workbench/api/common/extHostTreeViews.ts | 31 +++++++++---------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/vs/base/browser/ui/grid/grid.ts b/src/vs/base/browser/ui/grid/grid.ts index 2562b2fa153..821567ccf59 100644 --- a/src/vs/base/browser/ui/grid/grid.ts +++ b/src/vs/base/browser/ui/grid/grid.ts @@ -8,8 +8,7 @@ import { equals, tail2 as tail } from 'vs/base/common/arrays'; import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import 'vs/css!./gridview'; -import { Box, GridView, IGridViewOptions, IGridViewStyles, IView as IGridViewView, IViewSize, orthogonal, Sizing as GridViewSizing } from './gridview'; -import type { GridLocation } from 'vs/base/browser/ui/grid/gridview'; +import { Box, GridView, IGridViewOptions, IGridViewStyles, IView as IGridViewView, IViewSize, orthogonal, Sizing as GridViewSizing, GridLocation } from './gridview'; import type { SplitView, AutoSizing as SplitViewAutoSizing } from 'vs/base/browser/ui/splitview/splitview'; export { IViewSize, LayoutPriority, Orientation, orthogonal } from './gridview'; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts index 152aeb832ae..7b0b9bfcd21 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRendererFactory.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { MinimapCharRenderer } from 'vs/editor/browser/viewParts/minimap/minimapCharRenderer'; -import { allCharCodes } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; +import { allCharCodes, Constants } from 'vs/editor/browser/viewParts/minimap/minimapCharSheet'; import { prebakedMiniMaps } from 'vs/editor/browser/viewParts/minimap/minimapPreBaked'; -import { Constants } from './minimapCharSheet'; import { toUint8 } from 'vs/base/common/uint'; /** diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 844b0b63eae..3de4e43cbf9 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -5,7 +5,6 @@ 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'; @@ -14,7 +13,7 @@ import { CheckboxUpdate, DataTransferDTO, ExtHostTreeViewsShape, MainThreadTreeV import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions, TreeCommand, TreeViewPaneHandleArg, ITreeItemCheckboxState, NoTreeViewError } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { asPromise } from 'vs/base/common/async'; -import { TreeItemCollapsibleState, TreeItemCheckboxState, ThemeIcon, MarkdownString as MarkdownStringType, TreeItem, ViewBadge as ExtHostViewBadge } from 'vs/workbench/api/common/extHostTypes'; +import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; @@ -134,7 +133,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return treeView.badge; }, set badge(badge: vscode.ViewBadge | undefined) { - if ((badge !== undefined) && ExtHostViewBadge.isViewBadge(badge)) { + if ((badge !== undefined) && extHostTypes.ViewBadge.isViewBadge(badge)) { treeView.badge = { value: Math.floor(Math.abs(badge.value)), tooltip: badge.tooltip @@ -203,7 +202,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { return Promise.reject(new NoTreeViewError(sourceViewId)); } - const treeDataTransfer = await this.addAdditionalTransferItems(new types.DataTransfer(), treeView, sourceTreeItemHandles, token, operationUuid); + const treeDataTransfer = await this.addAdditionalTransferItems(new extHostTypes.DataTransfer(), treeView, sourceTreeItemHandles, token, operationUuid); if (!treeDataTransfer || token.isCancellationRequested) { return; } @@ -514,21 +513,21 @@ class ExtHostTreeView extends Disposable { } async setCheckboxState(checkboxUpdates: CheckboxUpdate[]) { - type CheckboxUpdateWithItem = { extensionItem: NonNullable; treeItem: vscode.TreeItem; newState: TreeItemCheckboxState }; + type CheckboxUpdateWithItem = { extensionItem: NonNullable; treeItem: vscode.TreeItem; newState: extHostTypes.TreeItemCheckboxState }; const items = (await Promise.all(checkboxUpdates.map(async checkboxUpdate => { const extensionItem = this.getExtensionElement(checkboxUpdate.treeItemHandle); if (extensionItem) { return { extensionItem: extensionItem, treeItem: await this.dataProvider.getTreeItem(extensionItem), - newState: checkboxUpdate.newState ? TreeItemCheckboxState.Checked : TreeItemCheckboxState.Unchecked + newState: checkboxUpdate.newState ? extHostTypes.TreeItemCheckboxState.Checked : extHostTypes.TreeItemCheckboxState.Unchecked }; } return Promise.resolve(undefined); }))).filter((item): item is CheckboxUpdateWithItem => item !== undefined); items.forEach(item => { - item.treeItem.checkboxState = item.newState ? TreeItemCheckboxState.Checked : TreeItemCheckboxState.Unchecked; + item.treeItem.checkboxState = item.newState ? extHostTypes.TreeItemCheckboxState.Checked : extHostTypes.TreeItemCheckboxState.Unchecked; }); this._onDidChangeCheckboxState.fire({ items: items.map(item => [item.extensionItem, item.newState]) }); @@ -767,7 +766,7 @@ class ExtHostTreeView extends Disposable { } private getTooltip(tooltip?: string | vscode.MarkdownString): string | IMarkdownString | undefined { - if (MarkdownStringType.isMarkdownString(tooltip)) { + if (extHostTypes.MarkdownString.isMarkdownString(tooltip)) { return MarkdownString.from(tooltip); } return tooltip; @@ -781,7 +780,7 @@ class ExtHostTreeView extends Disposable { if (extensionTreeItem.checkboxState === undefined) { return undefined; } - let checkboxState: TreeItemCheckboxState; + let checkboxState: extHostTypes.TreeItemCheckboxState; let tooltip: string | undefined = undefined; let accessibilityInformation: IAccessibilityInformation | undefined = undefined; if (typeof extensionTreeItem.checkboxState === 'number') { @@ -791,11 +790,11 @@ class ExtHostTreeView extends Disposable { tooltip = extensionTreeItem.checkboxState.tooltip; accessibilityInformation = extensionTreeItem.checkboxState.accessibilityInformation; } - return { isChecked: checkboxState === TreeItemCheckboxState.Checked, tooltip, accessibilityInformation }; + return { isChecked: checkboxState === extHostTypes.TreeItemCheckboxState.Checked, tooltip, accessibilityInformation }; } private validateTreeItem(extensionTreeItem: vscode.TreeItem) { - if (!TreeItem.isTreeItem(extensionTreeItem, this.extension)) { + if (!extHostTypes.TreeItem.isTreeItem(extensionTreeItem, this.extension)) { throw new Error(`Extension ${this.extension.identifier.value} has provided an invalid tree item.`); } } @@ -817,7 +816,7 @@ class ExtHostTreeView extends Disposable { icon, iconDark: this.getDarkIconPath(extensionTreeItem) || icon, themeIcon: this.getThemeIcon(extensionTreeItem), - collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState, + collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? extHostTypes.TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState, accessibilityInformation: extensionTreeItem.accessibilityInformation, checkbox: this.getCheckbox(extensionTreeItem), }; @@ -832,8 +831,8 @@ class ExtHostTreeView extends Disposable { }; } - private getThemeIcon(extensionTreeItem: vscode.TreeItem): ThemeIcon | undefined { - return extensionTreeItem.iconPath instanceof ThemeIcon ? extensionTreeItem.iconPath : undefined; + private getThemeIcon(extensionTreeItem: vscode.TreeItem): extHostTypes.ThemeIcon | undefined { + return extensionTreeItem.iconPath instanceof extHostTypes.ThemeIcon ? extensionTreeItem.iconPath : undefined; } private createHandle(element: T, { id, label, resourceUri }: vscode.TreeItem, parent: TreeNode | Root, returnFirst?: boolean): TreeItemHandle { @@ -865,7 +864,7 @@ class ExtHostTreeView extends Disposable { } private getLightIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined { - if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon)) { + if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof extHostTypes.ThemeIcon)) { if (typeof extensionTreeItem.iconPath === 'string' || URI.isUri(extensionTreeItem.iconPath)) { return this.getIconPath(extensionTreeItem.iconPath); @@ -876,7 +875,7 @@ class ExtHostTreeView extends Disposable { } private getDarkIconPath(extensionTreeItem: vscode.TreeItem): URI | undefined { - if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof ThemeIcon) && (<{ light: string | URI; dark: string | URI }>extensionTreeItem.iconPath).dark) { + if (extensionTreeItem.iconPath && !(extensionTreeItem.iconPath instanceof extHostTypes.ThemeIcon) && (<{ light: string | URI; dark: string | URI }>extensionTreeItem.iconPath).dark) { return this.getIconPath((<{ light: string | URI; dark: string | URI }>extensionTreeItem.iconPath).dark); } return undefined; From e501709a2f762285b8c51b975e5aef4812efee1c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 07:48:10 -0700 Subject: [PATCH 498/607] Fix leaks in command detection capability test --- .../terminal/test/common/requestStore.test.ts | 2 +- .../commandDetectionCapability.test.ts | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/vs/platform/terminal/test/common/requestStore.test.ts b/src/vs/platform/terminal/test/common/requestStore.test.ts index 3da52600d94..3d0301d55b0 100644 --- a/src/vs/platform/terminal/test/common/requestStore.test.ts +++ b/src/vs/platform/terminal/test/common/requestStore.test.ts @@ -12,8 +12,8 @@ import { LogService } from 'vs/platform/log/common/logService'; import { RequestStore } from 'vs/platform/terminal/common/requestStore'; suite('RequestStore', () => { - let instantiationService: TestInstantiationService; let disposables: DisposableStore; + let instantiationService: TestInstantiationService; setup(() => { disposables = new DisposableStore(); diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts index 8ce2b64a810..e5f357d7780 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/commandDetectionCapability.test.ts @@ -13,6 +13,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { IContextMenuDelegate } from 'vs/base/browser/contextmenu'; import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; type TestTerminalCommandMatch = Pick & { marker: { line: number } }; @@ -23,6 +25,8 @@ class TestCommandDetectionCapability extends CommandDetectionCapability { } suite('CommandDetectionCapability', () => { + let disposables: DisposableStore; + let xterm: Terminal; let capability: TestCommandDetectionCapability; let addEvents: ITerminalCommand[]; @@ -57,20 +61,21 @@ suite('CommandDetectionCapability', () => { } setup(async () => { + disposables = new DisposableStore(); const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; xterm = new TerminalCtor({ allowProposedApi: true, cols: 80 }); - instantiationService = new TestInstantiationService(); + instantiationService = disposables.add(new TestInstantiationService()); instantiationService.stub(IContextMenuService, { showContextMenu(delegate: IContextMenuDelegate): void { } } as Partial); - capability = new TestCommandDetectionCapability(xterm, new NullLogService()); + capability = disposables.add(new TestCommandDetectionCapability(xterm, new NullLogService())); addEvents = []; capability.onCommandFinished(e => addEvents.push(e)); assertCommands([]); }); - teardown(() => { - instantiationService.dispose(); - }); + teardown(() => disposables.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); test('should not add commands when no capability methods are triggered', async () => { await writeP(xterm, 'foo\r\nbar\r\n'); From 67b8cf6e1eaddb04a4ad1f3fb11cb73d76fa0eba Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:01:34 -0700 Subject: [PATCH 499/607] Leak coverage in more terminal unit tests --- .../partialCommandDetectionCapability.ts | 12 +++--- .../capabilities/terminalCapabilityStore.ts | 8 ++-- .../test/node/terminalEnvironment.test.ts | 2 + .../terminal/browser/terminalConfigHelper.ts | 4 +- .../partialCommandDetectionCapability.test.ts | 3 ++ .../terminalCapabilityStore.test.ts | 14 ++++-- .../test/browser/terminalConfigHelper.test.ts | 43 +++++++++++-------- 7 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts index 5ec1e03c9b4..2b8f7cd0350 100644 --- a/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/partialCommandDetectionCapability.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IPartialCommandDetectionCapability, TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; // Importing types is safe in any layer // eslint-disable-next-line local/code-import-patterns @@ -20,27 +21,28 @@ const enum Constants { * This capability guesses where commands are based on where the cursor was when enter was pressed. * It's very hit or miss but it's often correct and better than nothing. */ -export class PartialCommandDetectionCapability implements IPartialCommandDetectionCapability { +export class PartialCommandDetectionCapability extends DisposableStore implements IPartialCommandDetectionCapability { readonly type = TerminalCapability.PartialCommandDetection; private readonly _commands: IMarker[] = []; get commands(): readonly IMarker[] { return this._commands; } - private readonly _onCommandFinished = new Emitter(); + private readonly _onCommandFinished = this.add(new Emitter()); readonly onCommandFinished = this._onCommandFinished.event; constructor( private readonly _terminal: Terminal, ) { - this._terminal.onData(e => this._onData(e)); - this._terminal.parser.registerCsiHandler({ final: 'J' }, params => { + super(); + this.add(this._terminal.onData(e => this._onData(e))); + this.add(this._terminal.parser.registerCsiHandler({ final: 'J' }, params => { if (params.length >= 1 && (params[0] === 2 || params[0] === 3)) { this._clearCommandsInViewport(); } // We don't want to override xterm.js' default behavior, just augment it return false; - }); + })); } private _onData(data: string): void { diff --git a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts index e630b8d898c..501ce1a78f3 100644 --- a/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts +++ b/src/vs/platform/terminal/common/capabilities/terminalCapabilityStore.ts @@ -102,9 +102,9 @@ export class TerminalCapabilityStoreMultiplexer extends Disposable implements IT this._onDidAddCapabilityType.fire(capability); this._onDidAddCapability.fire({ id: capability, capability: store.get(capability)! }); } - store.onDidAddCapabilityType(e => this._onDidAddCapabilityType.fire(e)); - store.onDidAddCapability(e => this._onDidAddCapability.fire(e)); - store.onDidRemoveCapabilityType(e => this._onDidRemoveCapabilityType.fire(e)); - store.onDidRemoveCapability(e => this._onDidRemoveCapability.fire(e)); + this._register(store.onDidAddCapabilityType(e => this._onDidAddCapabilityType.fire(e))); + this._register(store.onDidAddCapability(e => this._onDidAddCapability.fire(e))); + this._register(store.onDidRemoveCapabilityType(e => this._onDidRemoveCapabilityType.fire(e))); + this._register(store.onDidRemoveCapability(e => this._onDidRemoveCapability.fire(e))); } } diff --git a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts index e313005bf63..2f68a7be2df 100644 --- a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts @@ -6,6 +6,7 @@ import { deepStrictEqual, ok, strictEqual } from 'assert'; import { homedir, userInfo } from 'os'; import { isWindows } from 'vs/base/common/platform'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { ITerminalProcessOptions } from 'vs/platform/terminal/common/terminal'; @@ -21,6 +22,7 @@ const productService = { applicationName: 'vscode' } as IProductService; const defaultEnvironment = {}; suite('platform - terminalEnvironment', () => { + ensureNoDisposablesAreLeakedInTestSuite(); suite('getShellIntegrationInjection', () => { suite('should not enable', () => { // This test is only expected to work on Windows 10 build 18309 and above diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 39d3a0ceb03..fd27c75f4f7 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -50,11 +50,11 @@ export class TerminalConfigHelper extends Disposable implements IBrowserTerminal ) { super(); this._updateConfig(); - this._configurationService.onDidChangeConfiguration(e => { + this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TERMINAL_CONFIG_SECTION)) { this._updateConfig(); } - }); + })); if (isLinux) { if (navigator.userAgent.includes('Ubuntu')) { this._linuxDistro = LinuxDistro.Ubuntu; diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts index 947b6745852..26bb55f5654 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/partialCommandDetectionCapability.test.ts @@ -9,6 +9,7 @@ import type { IMarker, Terminal } from 'xterm'; import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; interface TestTerminal extends Terminal { _core: IXtermCore; @@ -33,6 +34,8 @@ suite('PartialCommandDetectionCapability', () => { capability.onCommandFinished(e => addEvents.push(e)); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('should not add commands when the cursor position is too close to the left side', async () => { assertCommands([]); xterm._core._onData.fire('\x0d'); diff --git a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts index 54c1489cb99..07ec5631f32 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/capabilities/terminalCapabilityStore.test.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { deepStrictEqual } from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { TerminalCapabilityStore, TerminalCapabilityStoreMultiplexer } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; @@ -53,6 +55,7 @@ suite('TerminalCapabilityStore', () => { }); suite('TerminalCapabilityStoreMultiplexer', () => { + let store: DisposableStore; let multiplexer: TerminalCapabilityStoreMultiplexer; let store1: TerminalCapabilityStore; let store2: TerminalCapabilityStore; @@ -60,16 +63,19 @@ suite('TerminalCapabilityStoreMultiplexer', () => { let removeEvents: TerminalCapability[]; setup(() => { - multiplexer = new TerminalCapabilityStoreMultiplexer(); + store = new DisposableStore(); + multiplexer = store.add(new TerminalCapabilityStoreMultiplexer()); multiplexer.onDidAddCapabilityType(e => addEvents.push(e)); multiplexer.onDidRemoveCapabilityType(e => removeEvents.push(e)); - store1 = new TerminalCapabilityStore(); - store2 = new TerminalCapabilityStore(); + store1 = store.add(new TerminalCapabilityStore()); + store2 = store.add(new TerminalCapabilityStore()); addEvents = []; removeEvents = []; }); - teardown(() => multiplexer.dispose()); + teardown(() => store.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); test('should fire events when capabilities are enabled', async () => { assertEvents(addEvents, []); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts index 799c2c74ba8..8764db8e257 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalConfigHelper.test.ts @@ -8,6 +8,8 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { LinuxDistro } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; class TestTerminalConfigHelper extends TerminalConfigHelper { set linuxDistro(distro: LinuxDistro) { @@ -16,6 +18,7 @@ class TestTerminalConfigHelper extends TerminalConfigHelper { } suite('Workbench - TerminalConfigHelper', function () { + let store: DisposableStore; let fixture: HTMLElement; // This suite has retries setup because the font-related tests flake only on GitHub actions, not @@ -24,15 +27,19 @@ suite('Workbench - TerminalConfigHelper', function () { this.retries(3); setup(() => { + store = new DisposableStore(); fixture = document.body; }); + teardown(() => store.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); test('TerminalConfigHelper - getFont fontFamily', () => { const configurationService = new TestConfigurationService({ editor: { fontFamily: 'foo' }, terminal: { integrated: { fontFamily: 'bar' } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontFamily, 'bar, monospace', 'terminal.integrated.fontFamily should be selected over editor.fontFamily'); }); @@ -42,7 +49,7 @@ suite('Workbench - TerminalConfigHelper', function () { editor: { fontFamily: 'foo' }, terminal: { integrated: { fontFamily: null } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.linuxDistro = LinuxDistro.Fedora; configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set'); @@ -53,7 +60,7 @@ suite('Workbench - TerminalConfigHelper', function () { editor: { fontFamily: 'foo' }, terminal: { integrated: { fontFamily: null } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.linuxDistro = LinuxDistro.Ubuntu; configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set'); @@ -64,7 +71,7 @@ suite('Workbench - TerminalConfigHelper', function () { editor: { fontFamily: 'foo' }, terminal: { integrated: { fontFamily: null } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontFamily, 'foo, monospace', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set'); }); @@ -82,7 +89,7 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize'); }); @@ -99,12 +106,12 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - let configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + let configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.linuxDistro = LinuxDistro.Ubuntu; configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it'); - configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it'); }); @@ -121,7 +128,7 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontSize, 100, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it'); }); @@ -138,12 +145,12 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - let configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + let configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.linuxDistro = LinuxDistro.Ubuntu; configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set'); - configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set'); }); @@ -161,7 +168,7 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight'); }); @@ -179,7 +186,7 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.getFont().lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set'); }); @@ -193,7 +200,7 @@ suite('Workbench - TerminalConfigHelper', function () { } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -206,7 +213,7 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -219,7 +226,7 @@ suite('Workbench - TerminalConfigHelper', function () { } } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); @@ -236,7 +243,7 @@ suite('Workbench - TerminalConfigHelper', function () { } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.configFontIsMonospace(), true, 'monospace is monospaced'); }); @@ -253,7 +260,7 @@ suite('Workbench - TerminalConfigHelper', function () { } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced'); }); @@ -270,7 +277,7 @@ suite('Workbench - TerminalConfigHelper', function () { } }); - const configHelper = new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!); + const configHelper = store.add(new TestTerminalConfigHelper(configurationService, null!, null!, null!, null!)); configHelper.panelContainer = fixture; assert.strictEqual(configHelper.configFontIsMonospace(), false, 'serif is not monospaced'); }); From 37aed2e103024d8992fe42cd95aded33db26e502 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 5 Sep 2023 08:19:22 -0700 Subject: [PATCH 500/607] eng: replace Event.chain with Event.chain2 (#192202) Followup from #192026 Removes the original leak-prone implementation of Event.chain and replaces it with `chain2` which only requires disposable of the resulting listener. --- src/vs/base/browser/ui/list/listWidget.ts | 14 +- .../browser/ui/selectBox/selectBoxCustom.ts | 27 ++-- src/vs/base/browser/ui/tree/abstractTree.ts | 12 +- src/vs/base/common/event.ts | 124 +++--------------- src/vs/base/test/common/event.test.ts | 10 +- .../browser/parameterHintsWidget.ts | 9 +- src/vs/platform/opener/browser/link.ts | 8 +- .../quickinput/browser/quickInputUtils.ts | 4 +- .../notifications/notificationsViewer.ts | 4 +- .../suggestEnabledInput.ts | 5 +- .../extensions/browser/extensionEditor.ts | 10 +- .../extensions/common/extensionsUtils.ts | 11 +- .../contrib/markers/browser/markersTable.ts | 10 +- .../services/notebookKeymapServiceImpl.ts | 11 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 10 +- 15 files changed, 95 insertions(+), 174 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 070cc08eeb4..ed0ad4678b7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -297,7 +297,7 @@ class KeyboardController implements IDisposable { @memoize private get onKeyDown(): Event { - return Event.chain2( + return Event.chain( this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => $.filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)) @@ -474,7 +474,7 @@ class TypeNavigationController implements IDisposable { let typing = false; - const onChar = Event.chain2(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => + const onChar = Event.chain(this.enabledDisposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => $.filter(e => !isInputElement(e.target as HTMLElement)) .filter(() => this.mode === TypeNavigationMode.Automatic || this.triggered) .map(event => new StandardKeyboardEvent(event)) @@ -580,12 +580,12 @@ class DOMFocusController implements IDisposable { private list: List, private view: IListView ) { - const onKeyDown = Event.chain2(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event, $ => $ + const onKeyDown = Event.chain(this.disposables.add(new DomEmitter(view.domNode, 'keydown')).event, $ => $ .filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)) ); - const onTab = Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Tab && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey)); + const onTab = Event.chain(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Tab && !e.ctrlKey && !e.metaKey && !e.shiftKey && !e.altKey)); onTab(this.onTab, this, this.disposables); } @@ -1360,13 +1360,13 @@ export class List implements ISpliceable, IDisposable { @memoize get onContextMenu(): Event> { let didJustPressContextMenuKey = false; - const fromKeyDown: Event = Event.chain2(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => + const fromKeyDown: Event = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keydown')).event, $ => $.map(e => new StandardKeyboardEvent(e)) .filter(e => didJustPressContextMenuKey = e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) .map(e => EventHelper.stop(e, true)) .filter(() => false)); - const fromKeyUp = Event.chain2(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event, $ => + const fromKeyUp = Event.chain(this.disposables.add(new DomEmitter(this.view.domNode, 'keyup')).event, $ => $.forEach(() => didJustPressContextMenuKey = false) .map(e => new StandardKeyboardEvent(e)) .filter(e => e.keyCode === KeyCode.ContextMenu || (e.shiftKey && e.keyCode === KeyCode.F10)) @@ -1379,7 +1379,7 @@ export class List implements ISpliceable, IDisposable { return { index, element, anchor, browserEvent }; })); - const fromMouse = Event.chain2(this.view.onContextMenu, $ => + const fromMouse = Event.chain(this.view.onContextMenu, $ => $.filter(_ => !didJustPressContextMenuKey) .map(({ element, index, browserEvent }) => ({ element, index, anchor: new StandardMouseEvent(browserEvent), browserEvent })) ); diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 898d768e870..819921c07ea 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -752,20 +752,21 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // SetUp list keyboard controller - control navigation, disabled items, focus const onKeyDown = this._register(new DomEmitter(this.selectDropDownListContainer, 'keydown')); - const onSelectDropDownKeyDown = Event.chain(onKeyDown.event) - .filter(() => this.selectList.length > 0) - .map(e => new StandardKeyboardEvent(e)); + const onSelectDropDownKeyDown = Event.chain(onKeyDown.event, $ => + $.filter(() => this.selectList.length > 0) + .map(e => new StandardKeyboardEvent(e)) + ); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(e => this.onEnter(e), this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Tab).on(e => this.onEnter(e), this)); // Tab should behave the same as enter, #79339 - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(e => this.onEscape(e), this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(e => this.onUpArrow(e), this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(e => this.onDownArrow(e), this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDown, this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUp, this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.Home).on(this.onHome, this)); - this._register(onSelectDropDownKeyDown.filter(e => e.keyCode === KeyCode.End).on(this.onEnd, this)); - this._register(onSelectDropDownKeyDown.filter(e => (e.keyCode >= KeyCode.Digit0 && e.keyCode <= KeyCode.KeyZ) || (e.keyCode >= KeyCode.Semicolon && e.keyCode <= KeyCode.NumpadDivide)).on(this.onCharacter, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Enter))(this.onEnter, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Tab))(this.onEnter, this)); // Tab should behave the same as enter, #79339 + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Escape))(this.onEscape, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.UpArrow))(this.onUpArrow, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.DownArrow))(this.onDownArrow, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.PageDown))(this.onPageDown, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.PageUp))(this.onPageUp, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Home))(this.onHome, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => e.keyCode === KeyCode.End))(this.onEnd, this)); + this._register(Event.chain(onSelectDropDownKeyDown, $ => $.filter(e => (e.keyCode >= KeyCode.Digit0 && e.keyCode <= KeyCode.KeyZ) || (e.keyCode >= KeyCode.Semicolon && e.keyCode <= KeyCode.NumpadDivide)))(this.onCharacter, this)); // SetUp list mouse controller - control navigation, disabled items, focus this._register(dom.addDisposableListener(this.selectList.getHTMLElement(), dom.EventType.POINTER_UP, e => this.onPointerUp(e))); diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 4327f53551f..5376fe98462 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -814,7 +814,7 @@ class FindWidget extends Disposable { this.mode = mode; const emitter = this._register(new DomEmitter(this.findInput.inputBox.inputElement, 'keydown')); - const onKeyDown = Event.chain2(emitter.event, $ => $.map(e => new StandardKeyboardEvent(e))); + const onKeyDown = Event.chain(emitter.event, $ => $.map(e => new StandardKeyboardEvent(e))); this._register(onKeyDown((e): any => { // Using equals() so we reserve modified keys for future use @@ -884,7 +884,7 @@ class FindWidget extends Disposable { })); })); - const onGrabKeyDown = Event.chain2(this._register(new DomEmitter(this.elements.grab, 'keydown')).event, $ => $.map(e => new StandardKeyboardEvent(e))); + const onGrabKeyDown = Event.chain(this._register(new DomEmitter(this.elements.grab, 'keydown')).event, $ => $.map(e => new StandardKeyboardEvent(e))); this._register(onGrabKeyDown((e): any => { let right: number | undefined; @@ -1640,14 +1640,14 @@ export abstract class AbstractTree implements IDisposable onDidChangeActiveNodes.input = activeNodesEmitter.event; if (_options.keyboardSupport !== false) { - const onKeyDown = Event.chain2(this.view.onKeyDown, $ => + const onKeyDown = Event.chain(this.view.onKeyDown, $ => $.filter(e => !isInputElement(e.target as HTMLElement)) .map(e => new StandardKeyboardEvent(e)) ); - Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.LeftArrow))(this.onLeftArrow, this, this.disposables); - Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.RightArrow))(this.onRightArrow, this, this.disposables); - Event.chain2(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Space))(this.onSpace, this, this.disposables); + Event.chain(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.LeftArrow))(this.onLeftArrow, this, this.disposables); + Event.chain(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.RightArrow))(this.onRightArrow, this, this.disposables); + Event.chain(onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Space))(this.onSpace, this, this.disposables); } if ((_options.findWidgetEnabled ?? true) && _options.keyboardNavigationLabelProvider && _options.contextViewProvider) { diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 7f3b4406d7a..2e5f7fc27a1 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -437,21 +437,33 @@ export namespace Event { return emitter.event; } - /** - * Implements event chaining, in a way that avoids having disposable - * intermediates at each step like {@link chain} does. + * Wraps the event in an {@link IChainableEvent}, allowing a more functional programming style. + * + * @example + * ``` + * // Normal + * const onEnterPressNormal = Event.filter( + * Event.map(onKeyPress.event, e => new StandardKeyboardEvent(e)), + * e.keyCode === KeyCode.Enter + * ).event; + * + * // Using chain + * const onEnterPressChain = Event.chain(onKeyPress.event, $ => $ + * .map(e => new StandardKeyboardEvent(e)) + * .filter(e => e.keyCode === KeyCode.Enter) + * ); + * ``` */ - export function chain2(event: Event, sythensize: ($: IChainableSythensis) => IChainableSythensis): Event { + export function chain(event: Event, sythensize: ($: IChainableSythensis) => IChainableSythensis): Event { const fn: Event = (listener, thisArgs, disposables) => { - const cs = new ChainableSynthesis(); - sythensize(cs); - return event(value => { + const cs = sythensize(new ChainableSynthesis()) as ChainableSynthesis; + return event(function (value) { const result = cs.evaluate(value); if (result !== HaltChainable) { - listener(result); + listener.call(thisArgs, result); } - }, thisArgs, disposables); + }, undefined, disposables); }; return fn; @@ -524,100 +536,6 @@ export namespace Event { latch(equals?: (a: T, b: T) => boolean): IChainableSythensis; } - export interface IChainableEvent extends IDisposable { - - event: Event; - map(fn: (i: T) => O): IChainableEvent; - forEach(fn: (i: T) => void): IChainableEvent; - filter(fn: (e: T) => boolean): IChainableEvent; - filter(fn: (e: T | R) => e is R): IChainableEvent; - reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent; - latch(): IChainableEvent; - debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number): IChainableEvent; - debounce(merge: (last: R | undefined, event: T) => R, delay?: number, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number): IChainableEvent; - on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[] | DisposableStore): IDisposable; - once(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable; - } - - class ChainableEvent implements IChainableEvent { - - private readonly disposables = new DisposableStore(); - - constructor(readonly event: Event) { } - - /** @see {@link Event.map} */ - map(fn: (i: T) => O): IChainableEvent { - return new ChainableEvent(map(this.event, fn, this.disposables)); - } - - /** @see {@link Event.forEach} */ - forEach(fn: (i: T) => void): IChainableEvent { - return new ChainableEvent(forEach(this.event, fn, this.disposables)); - } - - /** @see {@link Event.filter} */ - filter(fn: (e: T) => boolean): IChainableEvent; - filter(fn: (e: T | R) => e is R): IChainableEvent; - filter(fn: (e: T) => boolean): IChainableEvent { - return new ChainableEvent(filter(this.event, fn, this.disposables)); - } - - /** @see {@link Event.reduce} */ - reduce(merge: (last: R | undefined, event: T) => R, initial?: R): IChainableEvent { - return new ChainableEvent(reduce(this.event, merge, initial, this.disposables)); - } - - /** @see {@link Event.reduce} */ - latch(): IChainableEvent { - return new ChainableEvent(latch(this.event, undefined, this.disposables)); - } - - /** @see {@link Event.debounce} */ - debounce(merge: (last: T | undefined, event: T) => T, delay?: number, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number): IChainableEvent; - debounce(merge: (last: R | undefined, event: T) => R, delay?: number, leading?: boolean, flushOnListenerRemove?: boolean, leakWarningThreshold?: number): IChainableEvent; - debounce(merge: (last: R | undefined, event: T) => R, delay: number = 100, leading = false, flushOnListenerRemove = false, leakWarningThreshold?: number): IChainableEvent { - return new ChainableEvent(debounce(this.event, merge, delay, leading, flushOnListenerRemove, leakWarningThreshold, this.disposables)); - } - - /** - * Attach a listener to the event. - */ - on(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[] | DisposableStore) { - return this.event(listener, thisArgs, disposables); - } - - /** @see {@link Event.once} */ - once(listener: (e: T) => any, thisArgs: any, disposables: IDisposable[]) { - return once(this.event)(listener, thisArgs, disposables); - } - - dispose() { - this.disposables.dispose(); - } - } - - /** - * Wraps the event in an {@link IChainableEvent}, allowing a more functional programming style. - * - * @example - * ``` - * // Normal - * const onEnterPressNormal = Event.filter( - * Event.map(onKeyPress.event, e => new StandardKeyboardEvent(e)), - * e.keyCode === KeyCode.Enter - * ).event; - * - * // Using chain - * const onEnterPressChain = Event.chain(onKeyPress.event) - * .map(e => new StandardKeyboardEvent(e)) - * .filter(e => e.keyCode === KeyCode.Enter) - * .event; - * ``` - */ - export function chain(event: Event): IChainableEvent { - return new ChainableEvent(event); - } - export interface NodeEventEmitter { on(event: string | symbol, listener: Function): unknown; removeListener(event: string | symbol, listener: Function): unknown; diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index c2ef872140e..54b1e1f48c9 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -1527,7 +1527,7 @@ suite('Event utils', () => { }); test('maps', () => { - const ev = Event.chain2(em.event, $ => $.map(v => v * 2)); + const ev = Event.chain(em.event, $ => $.map(v => v * 2)); store.add(ev(v => calls.push(v))); em.fire(1); em.fire(2); @@ -1536,7 +1536,7 @@ suite('Event utils', () => { }); test('filters', () => { - const ev = Event.chain2(em.event, $ => $.filter(v => v % 2 === 0)); + const ev = Event.chain(em.event, $ => $.filter(v => v % 2 === 0)); store.add(ev(v => calls.push(v))); em.fire(1); em.fire(2); @@ -1546,7 +1546,7 @@ suite('Event utils', () => { }); test('reduces', () => { - const ev = Event.chain2(em.event, $ => $.reduce((acc, v) => acc + v, 0)); + const ev = Event.chain(em.event, $ => $.reduce((acc, v) => acc + v, 0)); store.add(ev(v => calls.push(v))); em.fire(1); em.fire(2); @@ -1556,7 +1556,7 @@ suite('Event utils', () => { }); test('latches', () => { - const ev = Event.chain2(em.event, $ => $.latch()); + const ev = Event.chain(em.event, $ => $.latch()); store.add(ev(v => calls.push(v))); em.fire(1); em.fire(1); @@ -1569,7 +1569,7 @@ suite('Event utils', () => { }); test('does everything', () => { - const ev = Event.chain2(em.event, $ => $ + const ev = Event.chain(em.event, $ => $ .filter(v => v % 2 === 0) .map(v => v * 2) .reduce((acc, v) => acc + v, 0) diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index 77094d0b005..c595faccc73 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -14,7 +14,7 @@ import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { assertIsDefined } from 'vs/base/common/types'; import 'vs/css!./parameterHints'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; -import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import * as languages from 'vs/editor/common/languages'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; @@ -130,9 +130,10 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { updateFont(); - this._register(Event.chain(this.editor.onDidChangeConfiguration.bind(this.editor)) - .filter(e => e.hasChanged(EditorOption.fontInfo)) - .on(updateFont, null)); + this._register(Event.chain( + this.editor.onDidChangeConfiguration.bind(this.editor), + $ => $.filter(e => e.hasChanged(EditorOption.fontInfo)) + )(updateFont)); this._register(this.editor.onDidLayoutChange(e => this.updateMaxHeight())); this.updateMaxHeight(); diff --git a/src/vs/platform/opener/browser/link.ts b/src/vs/platform/opener/browser/link.ts index 3b176459eed..2b455fa8dc6 100644 --- a/src/vs/platform/opener/browser/link.ts +++ b/src/vs/platform/opener/browser/link.ts @@ -93,10 +93,10 @@ export class Link extends Disposable { const onClickEmitter = this._register(new DomEmitter(this.el, 'click')); const onKeyPress = this._register(new DomEmitter(this.el, 'keypress')); - const onEnterPress = Event.chain(onKeyPress.event) - .map(e => new StandardKeyboardEvent(e)) - .filter(e => e.keyCode === KeyCode.Enter) - .event; + const onEnterPress = Event.chain(onKeyPress.event, $ => + $.map(e => new StandardKeyboardEvent(e)) + .filter(e => e.keyCode === KeyCode.Enter) + ); const onTap = this._register(new DomEmitter(this.el, TouchEventType.Tap)).event; this._register(Gesture.addTarget(this.el)); const onOpen = Event.any(onClickEmitter.event, onEnterPress, onTap); diff --git a/src/vs/platform/quickinput/browser/quickInputUtils.ts b/src/vs/platform/quickinput/browser/quickInputUtils.ts index 65df79a2d18..1c9566a6622 100644 --- a/src/vs/platform/quickinput/browser/quickInputUtils.ts +++ b/src/vs/platform/quickinput/browser/quickInputUtils.ts @@ -67,11 +67,11 @@ export function renderQuickInputDescription(description: string, container: HTML const onClick = actionHandler.disposables.add(new DomEmitter(anchor, dom.EventType.CLICK)).event; const onKeydown = actionHandler.disposables.add(new DomEmitter(anchor, dom.EventType.KEY_DOWN)).event; - const onSpaceOrEnter = actionHandler.disposables.add(Event.chain(onKeydown)).filter(e => { + const onSpaceOrEnter = Event.chain(onKeydown, $ => $.filter(e => { const event = new StandardKeyboardEvent(e); return event.equals(KeyCode.Space) || event.equals(KeyCode.Enter); - }).event; + })); actionHandler.disposables.add(Gesture.addTarget(anchor)); const onTap = actionHandler.disposables.add(new DomEmitter(anchor, GestureEventType.Tap)).event; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index cb9d9b7ce0e..fa8a759570c 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -171,11 +171,11 @@ class NotificationMessageRenderer { const onClick = actionHandler.toDispose.add(new DomEmitter(anchor, EventType.CLICK)).event; const onKeydown = actionHandler.toDispose.add(new DomEmitter(anchor, EventType.KEY_DOWN)).event; - const onSpaceOrEnter = actionHandler.toDispose.add(Event.chain(onKeydown)).filter(e => { + const onSpaceOrEnter = Event.chain(onKeydown, $ => $.filter(e => { const event = new StandardKeyboardEvent(e); return event.equals(KeyCode.Space) || event.equals(KeyCode.Enter); - }).event; + })); actionHandler.toDispose.add(Gesture.addTarget(anchor)); const onTap = actionHandler.toDispose.add(new DomEmitter(anchor, GestureEventType.Tap)).event; diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 8052a8efa93..85bb0e71779 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -209,9 +209,8 @@ export class SuggestEnabledInput extends Widget { this.stylingContainer.classList.remove('synthetic-focus'); }))); - const onKeyDownMonaco = Event.chain(this.inputWidget.onKeyDown); - this._register(onKeyDownMonaco.filter(e => e.keyCode === KeyCode.Enter).on(e => { e.preventDefault(); /** Do nothing. Enter causes new line which is not expected. */ }, this)); - this._register(onKeyDownMonaco.filter(e => e.keyCode === KeyCode.DownArrow && (isMacintosh ? e.metaKey : e.ctrlKey)).on(() => this._onShouldFocusResults.fire(), this)); + this._register(Event.chain(this.inputWidget.onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.Enter))(e => { e.preventDefault(); /** Do nothing. Enter causes new line which is not expected. */ }, this)); + this._register(Event.chain(this.inputWidget.onKeyDown, $ => $.filter(e => e.keyCode === KeyCode.DownArrow && (isMacintosh ? e.metaKey : e.ctrlKey)))(() => this._onShouldFocusResults.fire(), this)); let preexistingContent = this.getValue(); const inputWidgetModel = this.inputWidget.getModel(); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 16a37dc27c9..e9a2bb1d5f3 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -398,10 +398,12 @@ export class ExtensionEditor extends EditorPane { this._register(disposable); } - this._register(Event.chain(extensionActionBar.onDidRun) - .map(({ error }) => error) - .filter(error => !!error) - .on(this.onError, this)); + const onError = Event.chain(extensionActionBar.onDidRun, $ => + $.map(({ error }) => error) + .filter(error => !!error) + ); + + this._register(onError(this.onError, this)); const body = append(root, $('.body')); const navbar = new NavBar(body); diff --git a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts index 02afd35c0d8..1f56b42b32c 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts @@ -75,13 +75,12 @@ export class KeymapExtensions extends Disposable implements IWorkbenchContributi function onExtensionChanged(accessor: ServicesAccessor): Event { const extensionService = accessor.get(IExtensionManagementService); const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService); - const onDidInstallExtensions = Event.chain(extensionService.onDidInstallExtensions) - .filter(e => e.some(({ operation }) => operation === InstallOperation.Install)) - .map(e => e.map(({ identifier }) => identifier)) - .event; + const onDidInstallExtensions = Event.chain(extensionService.onDidInstallExtensions, $ => + $.filter(e => e.some(({ operation }) => operation === InstallOperation.Install)) + .map(e => e.map(({ identifier }) => identifier)) + ); return Event.debounce(Event.any( - Event.chain(Event.any(onDidInstallExtensions, Event.map(extensionService.onDidUninstallExtension, e => [e.identifier]))) - .event, + Event.any(onDidInstallExtensions, Event.map(extensionService.onDidUninstallExtension, e => [e.identifier])), Event.map(extensionEnablementService.onEnablementChanged, extensions => extensions.map(e => e.identifier)) ), (result: IExtensionIdentifier[] | undefined, identifiers: IExtensionIdentifier[]) => { result = result || []; diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index 2a2fc816574..059f7262f5c 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -326,11 +326,11 @@ export class MarkersTable extends Disposable implements IProblemsWidget { 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 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')!)) + ); const onListLeave = Event.map(this._register(new DomEmitter(list, 'mouseleave')).event, () => -1); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl.ts index 19de4fc4458..8682f88efc3 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookKeymapServiceImpl.ts @@ -22,13 +22,12 @@ import { distinct } from 'vs/base/common/arrays'; function onExtensionChanged(accessor: ServicesAccessor): Event { const extensionService = accessor.get(IExtensionManagementService); const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService); - const onDidInstallExtensions = Event.chain(extensionService.onDidInstallExtensions) - .filter(e => e.some(({ operation }) => operation === InstallOperation.Install)) - .map(e => e.map(({ identifier }) => identifier)) - .event; + const onDidInstallExtensions = Event.chain(extensionService.onDidInstallExtensions, $ => + $.filter(e => e.some(({ operation }) => operation === InstallOperation.Install)) + .map(e => e.map(({ identifier }) => identifier)) + ); return Event.debounce(Event.any( - Event.chain(Event.any(onDidInstallExtensions, Event.map(extensionService.onDidUninstallExtension, e => [e.identifier]))) - .event, + Event.any(onDidInstallExtensions, Event.map(extensionService.onDidUninstallExtension, e => [e.identifier])), Event.map(extensionEnablementService.onEnablementChanged, extensions => extensions.map(e => e.identifier)) ), (result: IExtensionIdentifier[] | undefined, identifiers: IExtensionIdentifier[]) => { result = result || (identifiers.length ? [identifiers[0]] : []); diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 45c801dff07..b6377ab09c0 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -862,10 +862,12 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu const disposables = new DisposableStore(); disposables.add(Event.once(this.widget.onDidClose)(this.close, this)); - Event.chain(model.onDidChange) - .filter(e => e.diff.length > 0) - .map(e => e.diff) - .event(this.onDidModelChange, this, disposables); + const onDidModelChange = Event.chain(model.onDidChange, $ => + $.filter(e => e.diff.length > 0) + .map(e => e.diff) + ); + + onDidModelChange(this.onDidModelChange, this, disposables); disposables.add(this.widget); disposables.add(toDisposable(() => { From 6f033d17eef4005dbfb9004d601cd47c272e1459 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:49:18 -0700 Subject: [PATCH 501/607] Leak in terminal instance/proc manager --- .../test/browser/terminalInstance.test.ts | 107 ++++++++++-------- .../browser/terminalInstanceService.test.ts | 3 + .../browser/terminalProcessManager.test.ts | 24 ++-- 3 files changed, 74 insertions(+), 60 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts index 295fb4bb119..bcbb193e128 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts @@ -20,34 +20,19 @@ import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilitie import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { Schemas } from 'vs/base/common/network'; import { TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; -export function createInstance(partial?: Partial): Pick { - const capabilities = new TerminalCapabilityStore(); - if (!isWindows) { - capabilities.add(TerminalCapability.NaiveCwdDetection, null!); - } - return { - shellLaunchConfig: {}, - cwd: 'cwd', - initialCwd: undefined, - processName: '', - sequence: undefined, - workspaceFolder: undefined, - staticTitle: undefined, - capabilities, - title: '', - description: '', - userHome: undefined, - ...partial - }; -} const root1 = '/foo/root1'; const ROOT_1 = fixPath(root1); const root2 = '/foo/root2'; const ROOT_2 = fixPath(root2); const emptyRoot = '/foo'; const ROOT_EMPTY = fixPath(emptyRoot); + suite('Workbench - TerminalInstance', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('parseExitResult', () => { test('should return no message for exit code = undefined', () => { deepStrictEqual( @@ -155,6 +140,7 @@ suite('Workbench - TerminalInstance', () => { }); }); suite('TerminalLabelComputer', () => { + let store: DisposableStore; let configurationService: TestConfigurationService; let terminalLabelComputer: TerminalLabelComputer; let instantiationService: TestInstantiationService; @@ -166,10 +152,33 @@ suite('Workbench - TerminalInstance', () => { let emptyWorkspace: Workspace; let capabilities: TerminalCapabilityStore; let configHelper: TerminalConfigHelper; + + function createInstance(partial?: Partial): Pick { + const capabilities = store.add(new TerminalCapabilityStore()); + if (!isWindows) { + capabilities.add(TerminalCapability.NaiveCwdDetection, null!); + } + return { + shellLaunchConfig: {}, + cwd: 'cwd', + initialCwd: undefined, + processName: '', + sequence: undefined, + workspaceFolder: undefined, + staticTitle: undefined, + capabilities, + title: '', + description: '', + userHome: undefined, + ...partial + }; + } + setup(async () => { - instantiationService = new TestInstantiationService(); + store = new DisposableStore(); + instantiationService = store.add(new TestInstantiationService()); instantiationService.stub(IWorkspaceContextService, new TestContextService()); - capabilities = new TerminalCapabilityStore(); + capabilities = store.add(new TerminalCapabilityStore()); if (!isWindows) { capabilities.add(TerminalCapability.NaiveCwdDetection, null!); } @@ -190,14 +199,12 @@ suite('Workbench - TerminalInstance', () => { emptyContextService.setWorkspace(emptyWorkspace); }); - teardown(() => { - instantiationService.dispose(); - }); + teardown(() => store.dispose()); test('should resolve to "" when the template variables are empty', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '', description: '' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: '' })); // TODO: // terminalLabelComputer.onLabelChanged(e => { @@ -209,80 +216,80 @@ suite('Workbench - TerminalInstance', () => { }); test('should resolve cwd', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '${cwd}', description: '${cwd}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, cwd: ROOT_1 })); strictEqual(terminalLabelComputer.title, ROOT_1); strictEqual(terminalLabelComputer.description, ROOT_1); }); test('should resolve workspaceFolder', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '${workspaceFolder}', description: '${workspaceFolder}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'zsh', workspaceFolder: { uri: URI.from({ scheme: Schemas.file, path: 'folder' }) } as IWorkspaceFolder })); strictEqual(terminalLabelComputer.title, 'folder'); strictEqual(terminalLabelComputer.description, 'folder'); }); test('should resolve local', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '${local}', description: '${local}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'zsh', shellLaunchConfig: { type: 'Local' } })); strictEqual(terminalLabelComputer.title, 'Local'); strictEqual(terminalLabelComputer.description, 'Local'); }); test('should resolve process', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '${process}', description: '${process}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'zsh' })); strictEqual(terminalLabelComputer.title, 'zsh'); strictEqual(terminalLabelComputer.description, 'zsh'); }); test('should resolve sequence', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '${sequence}', description: '${sequence}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, sequence: 'sequence' })); strictEqual(terminalLabelComputer.title, 'sequence'); strictEqual(terminalLabelComputer.description, 'sequence'); }); test('should resolve task', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${process}${separator}${task}', description: '${task}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'zsh', shellLaunchConfig: { type: 'Task' } })); strictEqual(terminalLabelComputer.title, 'zsh ~ Task'); strictEqual(terminalLabelComputer.description, 'Task'); }); test('should resolve separator', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${separator}', description: '${separator}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'zsh', shellLaunchConfig: { type: 'Task' } })); strictEqual(terminalLabelComputer.title, 'zsh'); strictEqual(terminalLabelComputer.description, ''); }); test('should always return static title when specified', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${process}', description: '${workspaceFolder}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'process', workspaceFolder: { uri: URI.from({ scheme: Schemas.file, path: 'folder' }) } as IWorkspaceFolder, staticTitle: 'my-title' })); strictEqual(terminalLabelComputer.title, 'my-title'); strictEqual(terminalLabelComputer.description, 'folder'); }); test('should provide cwdFolder for all cwds only when in multi-root', () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${process}${separator}${cwdFolder}', description: '${cwdFolder}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'process', workspaceFolder: { uri: URI.from({ scheme: Schemas.file, path: ROOT_1 }) } as IWorkspaceFolder, cwd: ROOT_1 })); // single-root, cwd is same as root strictEqual(terminalLabelComputer.title, 'process'); strictEqual(terminalLabelComputer.description, ''); // multi-root configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${process}${separator}${cwdFolder}', description: '${cwdFolder}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockMultiRootContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockMultiRootContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'process', workspaceFolder: { uri: URI.from({ scheme: Schemas.file, path: ROOT_1 }) } as IWorkspaceFolder, cwd: ROOT_2 })); if (isWindows) { strictEqual(terminalLabelComputer.title, 'process'); @@ -294,13 +301,13 @@ suite('Workbench - TerminalInstance', () => { }); test('should hide cwdFolder in single folder workspaces when cwd matches the workspace\'s default cwd even when slashes differ', async () => { configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' ~ ', title: '${process}${separator}${cwdFolder}', description: '${cwdFolder}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + configHelper = store.add(new TerminalConfigHelper(configurationService, null!, null!, null!, null!)); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'process', workspaceFolder: { uri: URI.from({ scheme: Schemas.file, path: ROOT_1 }) } as IWorkspaceFolder, cwd: ROOT_1 })); strictEqual(terminalLabelComputer.title, 'process'); strictEqual(terminalLabelComputer.description, ''); if (!isWindows) { - terminalLabelComputer = new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService); + terminalLabelComputer = store.add(new TerminalLabelComputer(configHelper, new TestFileService(), mockContextService)); terminalLabelComputer.refreshLabel(createInstance({ capabilities, processName: 'process', workspaceFolder: { uri: URI.from({ scheme: Schemas.file, path: ROOT_1 }) } as IWorkspaceFolder, cwd: ROOT_2 })); strictEqual(terminalLabelComputer.title, 'process ~ root2'); strictEqual(terminalLabelComputer.description, 'root2'); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts index b36530cb82b..0b14bd97a30 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstanceService.test.ts @@ -15,6 +15,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Workbench - TerminalInstanceService', () => { let instantiationService: TestInstantiationService; @@ -40,6 +41,8 @@ suite('Workbench - TerminalInstanceService', () => { instantiationService.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + suite('convertProfileToShellLaunchConfig', () => { test('should return an empty shell launch config when undefined is provided', () => { deepStrictEqual(terminalInstanceService.convertProfileToShellLaunchConfig(), {}); diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts index b7291fc85f4..8f4c4a1410b 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalProcessManager.test.ts @@ -14,12 +14,14 @@ import { IEnvironmentVariableService } from 'vs/workbench/contrib/terminal/commo import { EnvironmentVariableService } from 'vs/workbench/contrib/terminal/common/environmentVariableService'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; -import { ITerminalChildProcess } from 'vs/platform/terminal/common/terminal'; +import { ITerminalChildProcess, ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import { ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { NullLogService } from 'vs/platform/log/common/log'; class TestTerminalChildProcess implements ITerminalChildProcess { id: number = 0; @@ -73,19 +75,20 @@ class TestTerminalInstanceService implements Partial { env: any, windowsEnableConpty: boolean, shouldPersist: boolean - ) => new TestTerminalChildProcess(shouldPersist) + ) => new TestTerminalChildProcess(shouldPersist), + getLatency: () => Promise.resolve([]) } as any; } } suite('Workbench - TerminalProcessManager', () => { - let disposables: DisposableStore; + let store: DisposableStore; let instantiationService: ITestInstantiationService; let manager: TerminalProcessManager; setup(async () => { - disposables = new DisposableStore(); - instantiationService = workbenchInstantiationService(undefined, disposables); + store = new DisposableStore(); + instantiationService = workbenchInstantiationService(undefined, store); const configurationService = new TestConfigurationService(); await configurationService.setUserConfiguration('editor', { fontFamily: 'foo' }); await configurationService.setUserConfiguration('terminal', { @@ -99,17 +102,18 @@ suite('Workbench - TerminalProcessManager', () => { }); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IProductService, TestProductService); + instantiationService.stub(ITerminalLogService, new NullLogService()); instantiationService.stub(IEnvironmentVariableService, instantiationService.createInstance(EnvironmentVariableService)); instantiationService.stub(ITerminalProfileResolverService, TestTerminalProfileResolverService); instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService()); - const configHelper = instantiationService.createInstance(TerminalConfigHelper); - manager = instantiationService.createInstance(TerminalProcessManager, 1, configHelper, undefined, undefined, undefined); + const configHelper = store.add(instantiationService.createInstance(TerminalConfigHelper)); + manager = store.add(instantiationService.createInstance(TerminalProcessManager, 1, configHelper, undefined, undefined, undefined)); }); - teardown(() => { - disposables.dispose(); - }); + teardown(() => store.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); suite('process persistence', () => { suite('local', () => { From 201ef0530488c90c8b8d627b5310d7a0ead931d8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:19:40 -0700 Subject: [PATCH 502/607] Leak protection in terminal service test --- .../terminal/browser/terminalService.ts | 4 +- .../test/browser/terminalService.test.ts | 192 ++++++++---------- 2 files changed, 92 insertions(+), 104 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 3a8d8a3d34c..fa3a6cf51d3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -235,7 +235,7 @@ export class TerminalService extends Disposable implements ITerminalService { this.initializePrimaryBackend(); // Create async as the class depends on `this` - timeout(0).then(() => this._instantiationService.createInstance(TerminalEditorStyle, document.head)); + timeout(0).then(() => this._register(this._instantiationService.createInstance(TerminalEditorStyle, document.head))); } async showProfileQuickPick(type: 'setDefault' | 'createInstance', cwd?: string | URI): Promise { @@ -421,7 +421,7 @@ export class TerminalService extends Disposable implements ITerminalService { } } return new Promise(r => { - instance.onExit(() => r()); + Event.once(instance.onExit)(() => r()); instance.dispose(TerminalExitReason.User); }); } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts index 4ba34944a97..e2051ef04aa 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalService.test.ts @@ -5,14 +5,14 @@ import { fail } from 'assert'; import { Emitter } from 'vs/base/common/event'; -import { TerminalLocation } from 'vs/platform/terminal/common/terminal'; +import { ITerminalLogService, TerminalLocation } from 'vs/platform/terminal/common/terminal'; import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ContextKeyService } from 'vs/platform/contextkey/browser/contextKeyService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TestEditorService, TestLifecycleService, TestRemoteAgentService, TestTerminalEditorService, TestTerminalGroupService, TestTerminalInstanceService, TestTerminalProfileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestEditorService, TestEnvironmentService, TestLifecycleService, TestRemoteAgentService, TestTerminalEditorService, TestTerminalGroupService, TestTerminalInstanceService, TestTerminalProfileService } from 'vs/workbench/test/browser/workbenchTestServices'; import { ITerminalEditorService, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -22,14 +22,20 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { TestDialogService } from 'vs/platform/dialogs/test/common/testDialogService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('Workbench - TerminalService', () => { + let store: DisposableStore; let instantiationService: TestInstantiationService; let terminalService: TerminalService; let configurationService: TestConfigurationService; let dialogService: TestDialogService; setup(async () => { + store = new DisposableStore(); dialogService = new TestDialogService(); configurationService = new TestConfigurationService({ terminal: { @@ -39,106 +45,96 @@ suite('Workbench - TerminalService', () => { } }); - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IContextKeyService, instantiationService.createInstance(ContextKeyService)); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IThemeService, new TestThemeService()); + instantiationService.stub(ITerminalLogService, new NullLogService()); instantiationService.stub(IEditorService, new TestEditorService()); + instantiationService.stub(IEnvironmentService, TestEnvironmentService); instantiationService.stub(ITerminalEditorService, new TestTerminalEditorService()); instantiationService.stub(ITerminalGroupService, new TestTerminalGroupService()); instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService()); + instantiationService.stub(ITerminalInstanceService, 'getBackend', undefined); + instantiationService.stub(ITerminalInstanceService, 'getRegisteredBackends', []); instantiationService.stub(ITerminalProfileService, new TestTerminalProfileService()); instantiationService.stub(IRemoteAgentService, new TestRemoteAgentService()); instantiationService.stub(IRemoteAgentService, 'getConnection', null); instantiationService.stub(IDialogService, dialogService); - terminalService = instantiationService.createInstance(TerminalService); + terminalService = store.add(instantiationService.createInstance(TerminalService)); instantiationService.stub(ITerminalService, terminalService); }); - teardown(() => { - instantiationService.dispose(); - }); + teardown(() => store.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); suite('safeDisposeTerminal', () => { let onExitEmitter: Emitter; setup(() => { - onExitEmitter = new Emitter(); + onExitEmitter = store.add(new Emitter()); }); test('should not show prompt when confirmOnKill is never', async () => { - setConfirmOnKill(configurationService, 'never'); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Editor, - hasChildProcesses: true, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: true, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + await setConfirmOnKill(configurationService, 'never'); + await terminalService.safeDisposeTerminal({ + target: TerminalLocation.Editor, + hasChildProcesses: true, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); + await terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); }); test('should not show prompt when any terminal editor is closed (handled by editor itself)', async () => { - setConfirmOnKill(configurationService, 'editor'); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Editor, - hasChildProcesses: true, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); - setConfirmOnKill(configurationService, 'always'); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Editor, - hasChildProcesses: true, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + await setConfirmOnKill(configurationService, 'editor'); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Editor, + hasChildProcesses: true, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); + await setConfirmOnKill(configurationService, 'always'); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Editor, + hasChildProcesses: true, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); }); test('should not show prompt when confirmOnKill is editor and panel terminal is closed', async () => { - setConfirmOnKill(configurationService, 'editor'); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: true, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + await setConfirmOnKill(configurationService, 'editor'); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); }); test('should show prompt when confirmOnKill is panel and panel terminal is closed', async () => { - setConfirmOnKill(configurationService, 'panel'); + await setConfirmOnKill(configurationService, 'panel'); // No child process cases dialogService.setConfirmResult({ confirmed: false }); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: false, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); dialogService.setConfirmResult({ confirmed: true }); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: false, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); // Child process cases dialogService.setConfirmResult({ confirmed: false }); await terminalService.safeDisposeTerminal({ @@ -147,36 +143,30 @@ suite('Workbench - TerminalService', () => { dispose: () => fail() } as Partial as any); dialogService.setConfirmResult({ confirmed: true }); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: true, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); }); test('should show prompt when confirmOnKill is always and panel terminal is closed', async () => { - setConfirmOnKill(configurationService, 'always'); + await setConfirmOnKill(configurationService, 'always'); // No child process cases dialogService.setConfirmResult({ confirmed: false }); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: false, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); dialogService.setConfirmResult({ confirmed: true }); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: false, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: false, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); // Child process cases dialogService.setConfirmResult({ confirmed: false }); await terminalService.safeDisposeTerminal({ @@ -185,14 +175,12 @@ suite('Workbench - TerminalService', () => { dispose: () => fail() } as Partial as any); dialogService.setConfirmResult({ confirmed: true }); - await new Promise(r => { - terminalService.safeDisposeTerminal({ - target: TerminalLocation.Panel, - hasChildProcesses: true, - onExit: onExitEmitter.event, - dispose: () => r() - } as Partial as any); - }); + terminalService.safeDisposeTerminal({ + target: TerminalLocation.Panel, + hasChildProcesses: true, + onExit: onExitEmitter.event, + dispose: () => onExitEmitter.fire(undefined) + } as Partial as any); }); }); }); From 2e4a770fd50ce5ff0589eb59705b418de90f3cae Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:24:24 -0700 Subject: [PATCH 503/607] Leak terminalStatusList.test.ts --- .../test/browser/terminalStatusList.test.ts | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts index 21d76a53d16..af6a7c3fbe1 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalStatusList.test.ts @@ -11,23 +11,27 @@ import { spinningLoading } from 'vs/platform/theme/common/iconRegistry'; import { ThemeIcon } from 'vs/base/common/themables'; import { TerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { ITerminalStatus } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; function statusesEqual(list: TerminalStatusList, expected: [string, Severity][]) { deepStrictEqual(list.statuses.map(e => [e.id, e.severity]), expected); } suite('Workbench - TerminalStatusList', () => { + let store: DisposableStore; let list: TerminalStatusList; let configService: TestConfigurationService; setup(() => { + store = new DisposableStore(); configService = new TestConfigurationService(); - list = new TerminalStatusList(configService); + list = store.add(new TerminalStatusList(configService)); }); - teardown(() => { - list.dispose(); - }); + teardown(() => store.dispose()); + + ensureNoDisposablesAreLeakedInTestSuite(); test('primary', () => { strictEqual(list.primary?.id, undefined); @@ -72,7 +76,7 @@ suite('Workbench - TerminalStatusList', () => { test('onDidAddStatus', async () => { const result = await new Promise(r => { - list.onDidAddStatus(r); + store.add(list.onDidAddStatus(r)); list.add({ id: 'test', severity: Severity.Info }); }); deepStrictEqual(result, { id: 'test', severity: Severity.Info }); @@ -80,7 +84,7 @@ suite('Workbench - TerminalStatusList', () => { test('onDidRemoveStatus', async () => { const result = await new Promise(r => { - list.onDidRemoveStatus(r); + store.add(list.onDidRemoveStatus(r)); list.add({ id: 'test', severity: Severity.Info }); list.remove('test'); }); @@ -89,7 +93,7 @@ suite('Workbench - TerminalStatusList', () => { test('onDidChangePrimaryStatus', async () => { const result = await new Promise(r => { - list.onDidRemoveStatus(r); + store.add(list.onDidRemoveStatus(r)); list.add({ id: 'test', severity: Severity.Info }); list.remove('test'); }); @@ -132,8 +136,8 @@ suite('Workbench - TerminalStatusList', () => { test('add should fire onDidRemoveStatus if same status id with a different object reference was added', () => { const eventCalls: string[] = []; - list.onDidAddStatus(() => eventCalls.push('add')); - list.onDidRemoveStatus(() => eventCalls.push('remove')); + store.add(list.onDidAddStatus(() => eventCalls.push('add'))); + store.add(list.onDidRemoveStatus(() => eventCalls.push('remove'))); list.add({ id: 'test', severity: Severity.Info }); list.add({ id: 'test', severity: Severity.Info }); deepStrictEqual(eventCalls, [ From 4f8773173b416903a9cb97044d3e7994d6ff485a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 09:31:16 -0700 Subject: [PATCH 504/607] Xterm addon leaks --- .../commandDetectionCapability.ts | 4 +- .../common/xterm/shellIntegrationAddon.ts | 4 +- .../terminal/test/browser/terminalUri.test.ts | 3 + .../browser/xterm/decorationAddon.test.ts | 3 + .../browser/xterm/lineDataEventAddon.test.ts | 13 +- .../xterm/shellIntegrationAddon.test.ts | 114 +++++++++--------- 6 files changed, 80 insertions(+), 61 deletions(-) diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index f3c2519be9e..1105d761a0e 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -164,7 +164,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe // For a Windows backend we cannot listen to CSI J, instead we assume running clear or // cls will clear all commands in the viewport. This is not perfect but it's right most // of the time. - this.onBeforeCommandFinished(command => { + this._register(this.onBeforeCommandFinished(command => { if (this._isWindowsPty) { if (command.command.trim().toLowerCase() === 'clear' || command.command.trim().toLowerCase() === 'cls') { this._clearCommandsInViewport(); @@ -172,7 +172,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe this._onCurrentCommandInvalidated.fire({ reason: CommandInvalidationReason.Windows }); } } - }); + })); // For non-Windows backends we can just listen to CSI J which is what the clear command // typically emits. diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index dd97f30b522..fb070c971d3 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -194,7 +194,7 @@ const enum ITermOscPt { */ export class ShellIntegrationAddon extends Disposable implements IShellIntegration, ITerminalAddon { private _terminal?: Terminal; - readonly capabilities = new TerminalCapabilityStore(); + readonly capabilities = this._register(new TerminalCapabilityStore()); private _hasUpdatedTelemetry: boolean = false; private _activationTimeout: any; private _commonProtocolDisposables: IDisposable[] = []; @@ -225,7 +225,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati activate(xterm: Terminal) { this._terminal = xterm; - this.capabilities.add(TerminalCapability.PartialCommandDetection, new PartialCommandDetectionCapability(this._terminal)); + this.capabilities.add(TerminalCapability.PartialCommandDetection, this._register(new PartialCommandDetectionCapability(this._terminal))); this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.VSCode, data => this._handleVSCodeSequence(data))); this._register(xterm.parser.registerOscHandler(ShellIntegrationOscPs.ITerm, data => this._doHandleITermSequence(data))); this._commonProtocolDisposables.push( diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalUri.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalUri.test.ts index 1a5430116a2..2b01688244c 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalUri.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalUri.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { deepStrictEqual, strictEqual } from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { getInstanceFromResource, getTerminalResourcesFromDragEvent, getTerminalUri, IPartialDragEvent } from 'vs/workbench/contrib/terminal/browser/terminalUri'; function fakeDragEvent(data: string): IPartialDragEvent { @@ -17,6 +18,8 @@ function fakeDragEvent(data: string): IPartialDragEvent { } suite('terminalUri', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('getTerminalResourcesFromDragEvent', () => { test('should give undefined when no terminal resources is in event', () => { deepStrictEqual( diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts index b9306c6c0a7..bfbfebf1d3d 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/decorationAddon.test.ts @@ -20,6 +20,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { TestLifecycleService } from 'vs/workbench/test/browser/workbenchTestServices'; import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('DecorationAddon', () => { let decorationAddon: DecorationAddon; @@ -71,6 +72,8 @@ suite('DecorationAddon', () => { instantiationService.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + suite('registerDecoration', () => { test('should throw when command has no marker', async () => { throws(() => decorationAddon.registerCommandDecoration({ command: 'cd src', timestamp: Date.now(), hasOutput: () => false } as ITerminalCommand)); diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts index 8953411f4a9..e7099524039 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/lineDataEventAddon.test.ts @@ -9,22 +9,29 @@ import { OperatingSystem } from 'vs/base/common/platform'; import { deepStrictEqual } from 'assert'; import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('LineDataEventAddon', () => { let xterm: Terminal; let lineDataEventAddon: LineDataEventAddon; + let store: DisposableStore; + setup(() => store = new DisposableStore()); + teardown(() => store.dispose()); + ensureNoDisposablesAreLeakedInTestSuite(); + suite('onLineData', () => { let events: string[]; setup(async () => { const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - xterm = new TerminalCtor({ allowProposedApi: true, cols: 4 }); - lineDataEventAddon = new LineDataEventAddon(); + xterm = store.add(new TerminalCtor({ allowProposedApi: true, cols: 4 })); + lineDataEventAddon = store.add(new LineDataEventAddon()); xterm.loadAddon(lineDataEventAddon); events = []; - lineDataEventAddon.onLineData(e => events.push(e)); + store.add(lineDataEventAddon.onLineData(e => events.push(e))); }); test('should fire when a non-wrapped line ends with a line feed', async () => { diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts index 5cba5252508..28562a28c9d 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts @@ -11,6 +11,8 @@ import { ITerminalCapabilityStore, TerminalCapability } from 'vs/platform/termin import { NullLogService } from 'vs/platform/log/common/log'; import { importAMDNodeModule } from 'vs/amdX'; import { writeP } from 'vs/workbench/contrib/terminal/browser/terminalTestHelpers'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestShellIntegrationAddon extends ShellIntegrationAddon { getCommandDetectionMock(terminal: Terminal): sinon.SinonMock { @@ -30,11 +32,15 @@ suite('ShellIntegrationAddon', () => { let shellIntegrationAddon: TestShellIntegrationAddon; let capabilities: ITerminalCapabilityStore; - setup(async () => { + let store: DisposableStore; + setup(() => store = new DisposableStore()); + teardown(() => store.dispose()); + ensureNoDisposablesAreLeakedInTestSuite(); + setup(async () => { const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); - shellIntegrationAddon = new TestShellIntegrationAddon('', true, undefined, new NullLogService()); + xterm = store.add(new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 })); + shellIntegrationAddon = store.add(new TestShellIntegrationAddon('', true, undefined, new NullLogService())); xterm.loadAddon(shellIntegrationAddon); capabilities = shellIntegrationAddon.capabilities; }); @@ -256,59 +262,59 @@ suite('ShellIntegrationAddon', () => { }); }); }); -}); -suite('deserializeMessage', () => { - // A single literal backslash, in order to avoid confusion about whether we are escaping test data or testing escapes. - const Backslash = '\\' as const; - const Newline = '\n' as const; - const Semicolon = ';' as const; + suite('deserializeMessage', () => { + // A single literal backslash, in order to avoid confusion about whether we are escaping test data or testing escapes. + const Backslash = '\\' as const; + const Newline = '\n' as const; + const Semicolon = ';' as const; - type TestCase = [title: string, input: string, expected: string]; - const cases: TestCase[] = [ - ['empty', '', ''], - ['basic', 'value', 'value'], - ['space', 'some thing', 'some thing'], - ['escaped backslash', `${Backslash}${Backslash}`, Backslash], - ['non-initial escaped backslash', `foo${Backslash}${Backslash}`, `foo${Backslash}`], - ['two escaped backslashes', `${Backslash}${Backslash}${Backslash}${Backslash}`, `${Backslash}${Backslash}`], - ['escaped backslash amidst text', `Hello${Backslash}${Backslash}there`, `Hello${Backslash}there`], - ['backslash escaped literally and as hex', `${Backslash}${Backslash} is same as ${Backslash}x5c`, `${Backslash} is same as ${Backslash}`], - ['escaped semicolon', `${Backslash}x3b`, Semicolon], - ['non-initial escaped semicolon', `foo${Backslash}x3b`, `foo${Semicolon}`], - ['escaped semicolon (upper hex)', `${Backslash}x3B`, Semicolon], - ['escaped backslash followed by literal "x3b" is not a semicolon', `${Backslash}${Backslash}x3b`, `${Backslash}x3b`], - ['non-initial escaped backslash followed by literal "x3b" is not a semicolon', `foo${Backslash}${Backslash}x3b`, `foo${Backslash}x3b`], - ['escaped backslash followed by escaped semicolon', `${Backslash}${Backslash}${Backslash}x3b`, `${Backslash}${Semicolon}`], - ['escaped semicolon amidst text', `some${Backslash}x3bthing`, `some${Semicolon}thing`], - ['escaped newline', `${Backslash}x0a`, Newline], - ['non-initial escaped newline', `foo${Backslash}x0a`, `foo${Newline}`], - ['escaped newline (upper hex)', `${Backslash}x0A`, Newline], - ['escaped backslash followed by literal "x0a" is not a newline', `${Backslash}${Backslash}x0a`, `${Backslash}x0a`], - ['non-initial escaped backslash followed by literal "x0a" is not a newline', `foo${Backslash}${Backslash}x0a`, `foo${Backslash}x0a`], - ]; + type TestCase = [title: string, input: string, expected: string]; + const cases: TestCase[] = [ + ['empty', '', ''], + ['basic', 'value', 'value'], + ['space', 'some thing', 'some thing'], + ['escaped backslash', `${Backslash}${Backslash}`, Backslash], + ['non-initial escaped backslash', `foo${Backslash}${Backslash}`, `foo${Backslash}`], + ['two escaped backslashes', `${Backslash}${Backslash}${Backslash}${Backslash}`, `${Backslash}${Backslash}`], + ['escaped backslash amidst text', `Hello${Backslash}${Backslash}there`, `Hello${Backslash}there`], + ['backslash escaped literally and as hex', `${Backslash}${Backslash} is same as ${Backslash}x5c`, `${Backslash} is same as ${Backslash}`], + ['escaped semicolon', `${Backslash}x3b`, Semicolon], + ['non-initial escaped semicolon', `foo${Backslash}x3b`, `foo${Semicolon}`], + ['escaped semicolon (upper hex)', `${Backslash}x3B`, Semicolon], + ['escaped backslash followed by literal "x3b" is not a semicolon', `${Backslash}${Backslash}x3b`, `${Backslash}x3b`], + ['non-initial escaped backslash followed by literal "x3b" is not a semicolon', `foo${Backslash}${Backslash}x3b`, `foo${Backslash}x3b`], + ['escaped backslash followed by escaped semicolon', `${Backslash}${Backslash}${Backslash}x3b`, `${Backslash}${Semicolon}`], + ['escaped semicolon amidst text', `some${Backslash}x3bthing`, `some${Semicolon}thing`], + ['escaped newline', `${Backslash}x0a`, Newline], + ['non-initial escaped newline', `foo${Backslash}x0a`, `foo${Newline}`], + ['escaped newline (upper hex)', `${Backslash}x0A`, Newline], + ['escaped backslash followed by literal "x0a" is not a newline', `${Backslash}${Backslash}x0a`, `${Backslash}x0a`], + ['non-initial escaped backslash followed by literal "x0a" is not a newline', `foo${Backslash}${Backslash}x0a`, `foo${Backslash}x0a`], + ]; - cases.forEach(([title, input, expected]) => { - test(title, () => strictEqual(deserializeMessage(input), expected)); - }); -}); - -test('parseKeyValueAssignment', () => { - type TestCase = [title: string, input: string, expected: [key: string, value: string | undefined]]; - const cases: TestCase[] = [ - ['empty', '', ['', undefined]], - ['no "=" sign', 'some-text', ['some-text', undefined]], - ['empty value', 'key=', ['key', '']], - ['empty key', '=value', ['', 'value']], - ['normal', 'key=value', ['key', 'value']], - ['multiple "=" signs (1)', 'key==value', ['key', '=value']], - ['multiple "=" signs (2)', 'key=value===true', ['key', 'value===true']], - ['just a "="', '=', ['', '']], - ['just a "=="', '==', ['', '=']], - ]; - - cases.forEach(x => { - const [title, input, [key, value]] = x; - deepStrictEqual(parseKeyValueAssignment(input), { key, value }, title); + cases.forEach(([title, input, expected]) => { + test(title, () => strictEqual(deserializeMessage(input), expected)); + }); + }); + + test('parseKeyValueAssignment', () => { + type TestCase = [title: string, input: string, expected: [key: string, value: string | undefined]]; + const cases: TestCase[] = [ + ['empty', '', ['', undefined]], + ['no "=" sign', 'some-text', ['some-text', undefined]], + ['empty value', 'key=', ['key', '']], + ['empty key', '=value', ['', 'value']], + ['normal', 'key=value', ['key', 'value']], + ['multiple "=" signs (1)', 'key==value', ['key', '=value']], + ['multiple "=" signs (2)', 'key=value===true', ['key', 'value===true']], + ['just a "="', '=', ['', '']], + ['just a "=="', '==', ['', '=']], + ]; + + cases.forEach(x => { + const [title, input, [key, value]] = x; + deepStrictEqual(parseKeyValueAssignment(input), { key, value }, title); + }); }); }); From 43495ec026d28ea415e42d0eb0bb75a287bff2c2 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Sep 2023 10:08:30 -0700 Subject: [PATCH 505/607] Add unbalancedBracketScopes for markdown (#191844) Fixes #191843 --- extensions/markdown-basics/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/extensions/markdown-basics/package.json b/extensions/markdown-basics/package.json index 9021acac872..52588bbd362 100644 --- a/extensions/markdown-basics/package.json +++ b/extensions/markdown-basics/package.json @@ -81,7 +81,11 @@ "meta.embedded.block.typescriptreact": "typescriptreact", "meta.embedded.block.csharp": "csharp", "meta.embedded.block.fsharp": "fsharp" - } + }, + "unbalancedBracketScopes": [ + "markup.underline.link.markdown", + "punctuation.definition.list.begin.markdown" + ] } ], "snippets": [ From 368f5d88c62ec47c7ed761b24488658d24de8f16 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:14:42 -0700 Subject: [PATCH 506/607] XtermTerminal leaks --- .../terminal/browser/xterm/xtermTerminal.ts | 40 ++++++------ .../xterm/shellIntegrationAddon.test.ts | 8 +-- .../test/browser/xterm/xtermTerminal.test.ts | 62 +++++++++++++++---- 3 files changed, 74 insertions(+), 36 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 12aaa0b0bb6..450580f9a50 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -14,7 +14,7 @@ import * as dom from 'vs/base/browser/dom'; import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminalConfigHelper'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IShellIntegration, ITerminalLogService, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalFont, ITerminalConfiguration } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -115,7 +115,7 @@ function getFullBufferLineAsString(lineIndex: number, buffer: IBuffer): { lineDa * Wraps the xterm object with additional functionality. Interaction with the backing process is out * of the scope of this class. */ -export class XtermTerminal extends DisposableStore implements IXtermTerminal, IDetachedXtermTerminal, IInternalXtermTerminal { +export class XtermTerminal extends Disposable implements IXtermTerminal, IDetachedXtermTerminal, IInternalXtermTerminal { /** The raw xterm.js instance */ readonly raw: RawXtermTerminal; private _core: IXtermCore; @@ -138,7 +138,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID private _serializeAddon?: SerializeAddonType; private _imageAddon?: ImageAddonType; - private readonly _attachedDisposables = this.add(new DisposableStore()); + private readonly _attachedDisposables = this._register(new DisposableStore()); private readonly _anyTerminalFocusContextKey: IContextKey; private readonly _anyFocusedTerminalHasSelection: IContextKey; @@ -147,21 +147,21 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID get isStdinDisabled(): boolean { return !!this.raw.options.disableStdin; } - private readonly _onDidRequestRunCommand = this.add(new Emitter<{ command: ITerminalCommand; copyAsHtml?: boolean; noNewLine?: boolean }>()); + private readonly _onDidRequestRunCommand = this._register(new Emitter<{ command: ITerminalCommand; copyAsHtml?: boolean; noNewLine?: boolean }>()); readonly onDidRequestRunCommand = this._onDidRequestRunCommand.event; - private readonly _onDidRequestFocus = this.add(new Emitter()); + private readonly _onDidRequestFocus = this._register(new Emitter()); readonly onDidRequestFocus = this._onDidRequestFocus.event; - private readonly _onDidRequestSendText = this.add(new Emitter()); + private readonly _onDidRequestSendText = this._register(new Emitter()); readonly onDidRequestSendText = this._onDidRequestSendText.event; - private readonly _onDidRequestFreePort = this.add(new Emitter()); + private readonly _onDidRequestFreePort = this._register(new Emitter()); readonly onDidRequestFreePort = this._onDidRequestFreePort.event; - private readonly _onDidChangeFindResults = this.add(new Emitter<{ resultIndex: number; resultCount: number }>()); + private readonly _onDidChangeFindResults = this._register(new Emitter<{ resultIndex: number; resultCount: number }>()); readonly onDidChangeFindResults = this._onDidChangeFindResults.event; - private readonly _onDidChangeSelection = this.add(new Emitter()); + private readonly _onDidChangeSelection = this._register(new Emitter()); readonly onDidChangeSelection = this._onDidChangeSelection.event; - private readonly _onDidChangeFocus = this.add(new Emitter()); + private readonly _onDidChangeFocus = this._register(new Emitter()); readonly onDidChangeFocus = this._onDidChangeFocus.event; - private readonly _onDidDispose = this.add(new Emitter()); + private readonly _onDidDispose = this._register(new Emitter()); readonly onDidDispose = this._onDidDispose.event; get markTracker(): IMarkTracker { return this._markNavigationAddon; } @@ -209,7 +209,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID const config = this._configHelper.config; const editorOptions = this._configurationService.getValue('editor'); - this.raw = this.add(new xtermCtor({ + this.raw = this._register(new xtermCtor({ allowProposedApi: true, cols, rows, @@ -244,7 +244,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this._updateSmoothScrolling(); this._core = (this.raw as any)._core as IXtermCore; - this.add(this._configurationService.onDidChangeConfiguration(async e => { + this._register(this._configurationService.onDidChangeConfiguration(async e => { if (e.affectsConfiguration(TerminalSettingId.GpuAcceleration)) { XtermTerminal._suggestedRendererType = undefined; } @@ -256,11 +256,11 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID } })); - this.add(this._themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); - this.add(this._logService.onDidChangeLogLevel(e => this.raw.options.logLevel = vscodeToXtermLogLevel(e))); + this._register(this._themeService.onDidColorThemeChange(theme => this._updateTheme(theme))); + this._register(this._logService.onDidChangeLogLevel(e => this.raw.options.logLevel = vscodeToXtermLogLevel(e))); // Refire events - this.add(this.raw.onSelectionChange(() => { + this._register(this.raw.onSelectionChange(() => { this._onDidChangeSelection.fire(); if (this.isFocused) { this._anyFocusedTerminalHasSelection.set(this.raw.hasSelection()); @@ -272,7 +272,7 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID this._markNavigationAddon = this._instantiationService.createInstance(MarkNavigationAddon, _capabilities); this.raw.loadAddon(this._markNavigationAddon); this._decorationAddon = this._instantiationService.createInstance(DecorationAddon, this._capabilities); - this._decorationAddon.onDidRequestRunCommand(e => this._onDidRequestRunCommand.fire(e)); + this._register(this._decorationAddon.onDidRequestRunCommand(e => this._onDidRequestRunCommand.fire(e))); this.raw.loadAddon(this._decorationAddon); this._shellIntegrationAddon = new ShellIntegrationAddon(shellIntegrationNonce, disableShellIntegrationReporting, this._telemetryService, this._logService); this.raw.loadAddon(this._shellIntegrationAddon); @@ -283,12 +283,12 @@ export class XtermTerminal extends DisposableStore implements IXtermTerminal, ID // Load the suggest addon, this should be loaded regardless of the setting as the sequences // may still come in if (this._terminalSuggestWidgetVisibleContextKey) { - this._suggestAddon = this._instantiationService.createInstance(SuggestAddon, this._terminalSuggestWidgetVisibleContextKey); + this._suggestAddon = this._register(this._instantiationService.createInstance(SuggestAddon, this._terminalSuggestWidgetVisibleContextKey)); this.raw.loadAddon(this._suggestAddon); - this._suggestAddon.onAcceptedCompletion(async text => { + this._register(this._suggestAddon.onAcceptedCompletion(async text => { this._onDidRequestFocus.fire(); this._onDidRequestSendText.fire(text); - }); + })); } } diff --git a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts index 28562a28c9d..1fcf433b59c 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/xterm/shellIntegrationAddon.test.ts @@ -28,15 +28,15 @@ class TestShellIntegrationAddon extends ShellIntegrationAddon { } suite('ShellIntegrationAddon', () => { - let xterm: Terminal; - let shellIntegrationAddon: TestShellIntegrationAddon; - let capabilities: ITerminalCapabilityStore; - let store: DisposableStore; setup(() => store = new DisposableStore()); teardown(() => store.dispose()); ensureNoDisposablesAreLeakedInTestSuite(); + let xterm: Terminal; + let shellIntegrationAddon: TestShellIntegrationAddon; + let capabilities: ITerminalCapabilityStore; + setup(async () => { const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; xterm = store.add(new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 })); 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 204505dd1e6..d31face6263 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 @@ -33,6 +33,9 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { Color, RGBA } from 'vs/base/common/color'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import type { Suite } from 'mocha'; // eslint-disable-line local/code-import-patterns class TestWebglAddon implements WebglAddon { static shouldThrow = false; @@ -91,7 +94,45 @@ const defaultTerminalConfig: Partial = { unicodeVersion: '6' }; -suite('XtermTerminal', () => { + +interface NoLeakSuiteFunction { + (title: string, fn: (this: Suite, store: Pick) => void): Suite; + only(title: string, fn: (this: Suite, store: Pick) => void): Suite; + skip(title: string, fn: (this: Suite, store: Pick) => void): Suite | void; +} +function noLeakSuiteBaseFn(that: Suite, fn: (this: Suite, store: Pick) => void): void { + let store: DisposableStore; + // Wrap store as the suite function is called before it's initialized + const testContext = { + add(o: T): T { + return store.add(o); + } + }; + setup(() => store = new DisposableStore()); + teardown(() => store.dispose()); + ensureNoDisposablesAreLeakedInTestSuite(); + fn.bind(that)(testContext); +} +function noLeakSuiteFn(title: string, fn: (this: Suite, store: Pick) => void): Suite { + return suite(title, function () { + noLeakSuiteBaseFn(this, fn); + }); +} +noLeakSuiteFn.only = (title: string, fn: (this: Suite, store: Pick) => void): Suite => { + return suite.only(title, function () { // eslint-disable-line local/code-no-test-only + noLeakSuiteBaseFn(this, fn); + }); +}; +noLeakSuiteFn.skip = (title: string, fn: (this: Suite, store: Pick) => void): Suite | void => { + return suite.skip(title, function () { + noLeakSuiteBaseFn(this, fn); + }); +}; +const noLeakSuite: NoLeakSuiteFunction = noLeakSuiteFn; + + + +noLeakSuite('XtermTerminal', store => { let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let themeService: TestThemeService; @@ -113,29 +154,26 @@ suite('XtermTerminal', () => { themeService = new TestThemeService(); viewDescriptorService = new TestViewDescriptorService(); - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(ITerminalLogService, new NullLogService()); - instantiationService.stub(IStorageService, new TestStorageService()); + instantiationService.stub(IStorageService, store.add(new TestStorageService())); instantiationService.stub(IThemeService, themeService); instantiationService.stub(IViewDescriptorService, viewDescriptorService); - instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); + instantiationService.stub(IContextMenuService, store.add(instantiationService.createInstance(ContextMenuService))); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); - configHelper = instantiationService.createInstance(TerminalConfigHelper); + configHelper = store.add(instantiationService.createInstance(TerminalConfigHelper)); XTermBaseCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - xterm = instantiationService.createInstance(TestXtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); + const capabilityStore = store.add(new TerminalCapabilityStore()); + xterm = store.add(instantiationService.createInstance(TestXtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, capabilityStore, '', new MockContextKeyService().createKey('', true)!, true)); TestWebglAddon.shouldThrow = false; TestWebglAddon.isEnabled = false; }); - teardown(() => { - instantiationService.dispose(); - }); - test('should use fallback dimensions of 80x30', () => { strictEqual(xterm.raw.cols, 80); strictEqual(xterm.raw.rows, 30); @@ -147,7 +185,7 @@ suite('XtermTerminal', () => { [PANEL_BACKGROUND]: '#ff0000', [SIDE_BAR_BACKGROUND]: '#00ff00' })); - xterm = instantiationService.createInstance(XtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => new Color(new RGBA(255, 0, 0)) }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); + xterm = store.add(instantiationService.createInstance(XtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => new Color(new RGBA(255, 0, 0)) }, store.add(new TerminalCapabilityStore()), '', new MockContextKeyService().createKey('', true)!, true)); strictEqual(xterm.raw.options.theme?.background, '#ff0000'); }); test('should react to and apply theme changes', () => { @@ -176,7 +214,7 @@ suite('XtermTerminal', () => { 'terminal.ansiBrightCyan': '#150000', 'terminal.ansiBrightWhite': '#160000', })); - xterm = instantiationService.createInstance(XtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, new TerminalCapabilityStore(), '', new MockContextKeyService().createKey('', true)!, true); + xterm = store.add(instantiationService.createInstance(XtermTerminal, XTermBaseCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, store.add(new TerminalCapabilityStore()), '', new MockContextKeyService().createKey('', true)!, true)); deepStrictEqual(xterm.raw.options.theme, { background: undefined, foreground: '#000200', From 3f791694da2bbc1fc0b42b05a8be261fe9bbebf9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:28:45 -0700 Subject: [PATCH 507/607] Return an optional DisposableStore from leak fn Part of #190503 --- src/vs/base/test/common/utils.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index 62508dcf7f1..03641b0c23a 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, IDisposableTracker, setDisposableTracker } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, IDisposableTracker, setDisposableTracker } from 'vs/base/common/lifecycle'; import { join } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -128,21 +128,34 @@ export class DisposableTracker implements IDisposableTracker { * * Use `markAsSingleton` if disposable singletons are created lazily that are allowed to outlive the test. * Make sure that the singleton properly registers all child disposables so that they are excluded too. + * + * @returns A {@link DisposableStore} that can optionally be used to track disposables in the test. + * This will be automatically disposed on test teardown. */ -export function ensureNoDisposablesAreLeakedInTestSuite() { +export function ensureNoDisposablesAreLeakedInTestSuite(): Pick { let tracker: DisposableTracker | undefined; + let store: DisposableStore; setup(() => { + store = new DisposableStore(); tracker = new DisposableTracker(); setDisposableTracker(tracker); }); teardown(function (this: import('mocha').Context) { + store.dispose(); setDisposableTracker(null); - if (this.currentTest?.state !== 'failed') { tracker!.ensureNoLeakingDisposables(); } }); + + // Wrap store as the suite function is called before it's initialized + const testContext = { + add(o: T): T { + return store.add(o); + } + }; + return testContext; } export function throwIfDisposablesAreLeaked(body: () => void): void { From 03a98986ec0b4a14c71783234b2b8041d088192d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:28:45 -0700 Subject: [PATCH 508/607] Return an optional DisposableStore from leak fn Part of #190503 --- src/vs/base/test/common/utils.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index 62508dcf7f1..03641b0c23a 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, IDisposableTracker, setDisposableTracker } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, IDisposableTracker, setDisposableTracker } from 'vs/base/common/lifecycle'; import { join } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -128,21 +128,34 @@ export class DisposableTracker implements IDisposableTracker { * * Use `markAsSingleton` if disposable singletons are created lazily that are allowed to outlive the test. * Make sure that the singleton properly registers all child disposables so that they are excluded too. + * + * @returns A {@link DisposableStore} that can optionally be used to track disposables in the test. + * This will be automatically disposed on test teardown. */ -export function ensureNoDisposablesAreLeakedInTestSuite() { +export function ensureNoDisposablesAreLeakedInTestSuite(): Pick { let tracker: DisposableTracker | undefined; + let store: DisposableStore; setup(() => { + store = new DisposableStore(); tracker = new DisposableTracker(); setDisposableTracker(tracker); }); teardown(function (this: import('mocha').Context) { + store.dispose(); setDisposableTracker(null); - if (this.currentTest?.state !== 'failed') { tracker!.ensureNoLeakingDisposables(); } }); + + // Wrap store as the suite function is called before it's initialized + const testContext = { + add(o: T): T { + return store.add(o); + } + }; + return testContext; } export function throwIfDisposablesAreLeaked(body: () => void): void { From bf605359b0c5b9a33e81614dd947fbafe6cb480f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:40:00 -0700 Subject: [PATCH 509/607] Add separator to test output This fixes terminal link detection --- src/vs/base/test/common/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index 62508dcf7f1..c89e899b2f0 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -111,7 +111,7 @@ export class DisposableTracker implements IDisposableTracker { const firstLeaking = leaking.slice(0, count); const remainingCount = leaking.length - count; - const separator = '--------------------\n\n'; + const separator = '\n--------------------\n\n'; let s = firstLeaking.map(l => l.source).join(separator); if (remainingCount > 0) { s += `${separator}+ ${remainingCount} more`; From b29799c8cb2ebf9b94b0d4756e09388990ded54c Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Tue, 5 Sep 2023 10:51:14 -0700 Subject: [PATCH 510/607] List widget onInput cleanup (#191882) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * code cleanup * cleanup part 2 * Apply suggestions from code review --------- Co-authored-by: João Moreno --- src/vs/base/browser/ui/list/listWidget.ts | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index ed0ad4678b7..1bd342c99c7 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -539,14 +539,21 @@ class TypeNavigationController implements IDisposable { if (this.list.options.typeNavigationEnabled) { if (typeof labelStr !== 'undefined') { - const prefix = matchesPrefix(word, labelStr); + + // If prefix is found, focus and return early + if (matchesPrefix(word, labelStr)) { + this.previouslyFocused = start; + this.list.setFocus([index]); + this.list.reveal(index); + return; + } + const fuzzy = matchesFuzzy2(word, labelStr); if (fuzzy) { const fuzzyScore = fuzzy[0].end - fuzzy[0].start; - // ensures that when fuzzy matching, doesn't clash with prefix matching (1 input vs 1+ should be prefix and fuzzy respecitvely). Also makes sure that exact matches are prioritized. - if (prefix || (fuzzyScore > 1 && fuzzy.length === 1)) { + if (fuzzyScore > 1 && fuzzy.length === 1) { this.previouslyFocused = start; this.list.setFocus([index]); this.list.reveal(index); @@ -554,13 +561,11 @@ class TypeNavigationController implements IDisposable { } } } - } else { - if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { - this.previouslyFocused = start; - this.list.setFocus([index]); - this.list.reveal(index); - return; - } + } else if (typeof labelStr === 'undefined' || matchesPrefix(word, labelStr)) { + this.previouslyFocused = start; + this.list.setFocus([index]); + this.list.reveal(index); + return; } } } @@ -1005,7 +1010,6 @@ export interface IListOptions extends IListOptionsUpdate { readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider; readonly keyboardNavigationDelegate?: IKeyboardNavigationDelegate; readonly keyboardSupport?: boolean; - readonly keyboardNavigationEnabled?: boolean; readonly multipleSelectionController?: IMultipleSelectionController; readonly styleController?: (suffix: string) => IStyleController; readonly accessibilityProvider?: IListAccessibilityProvider; From 2a85f13d7473ceaeffb70df80f6d4b806000d51f Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 5 Sep 2023 20:08:43 +0200 Subject: [PATCH 511/607] undo `removeSession` part --- src/vs/workbench/api/browser/mainThreadDebugService.ts | 6 +++--- src/vs/workbench/contrib/debug/browser/debugService.ts | 3 +-- src/vs/workbench/contrib/debug/common/debugModel.ts | 7 ------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index bb8ceab13b2..84f0e2e038d 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -56,7 +56,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb this._proxy.$acceptDebugSessionTerminated(this.getSessionDto(session)); this._sessions.delete(session.getId()); for (const [handle, value] of this._debugAdapters) { - if (value._session === session) { + if (value.session === session) { this._debugAdapters.delete(handle); // break; } @@ -428,7 +428,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb */ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { - constructor(private readonly _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, readonly _session: IDebugSession) { + constructor(private readonly _ds: MainThreadDebugService, private _handle: number, private _proxy: ExtHostDebugServiceShape, readonly session: IDebugSession) { super(); } @@ -441,7 +441,7 @@ class ExtensionHostDebugAdapter extends AbstractDebugAdapter { } startSession(): Promise { - return Promise.resolve(this._proxy.$startDASession(this._handle, this._ds.getSessionDto(this._session))); + return Promise.resolve(this._proxy.$startDASession(this._handle, this._ds.getSessionDto(this.session))); } sendMessage(message: DebugProtocol.ProtocolMessage): void { diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 0851c2b08a0..7e239aaa285 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -698,7 +698,6 @@ export class DebugService implements IDebugService { } this.endInitializingState(); this.cancelTokens(session.getId()); - this.model.removeSession(session); this._onDidEndSession.fire(session); const focusedSession = this.viewModel.focusedSession; @@ -728,7 +727,7 @@ export class DebugService implements IDebugService { this.model.removeExceptionBreakpointsForSession(session.getId()); sessionStore.dispose(); - session.dispose(); + // session.dispose(); TODO@roblourens })); } diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 6c700353cb9..15030ce7822 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -1264,13 +1264,6 @@ export class DebugModel extends Disposable implements IDebugModel { this._onDidChangeCallStack.fire(undefined); } - removeSession(session: IDebugSession): void { - const idx = this.sessions.indexOf(session); - if (idx >= 0) { - this.sessions.splice(idx, 1); - } - } - get onDidChangeBreakpoints(): Event { return this._onDidChangeBreakpoints.event; } From 9d0779ad5b16ab27539c9db6d731c8ba9d219c8b Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Sep 2023 11:22:22 -0700 Subject: [PATCH 512/607] Restore default paste fallback (#192218) Fixes #192196 --- .../contrib/dropOrPasteInto/browser/copyPasteController.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts index 30e79607eb1..f85e595c4ff 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -283,6 +283,12 @@ export class CopyPasteController extends Disposable implements IEditorContributi return; } + // If the only edit returned is a text edit, use the default paste handler + if (providerEdits.length === 1 && providerEdits[0].providerId === 'text') { + await this.applyDefaultPasteHandler(dataTransfer, metadata, tokenSource.token); + return; + } + if (providerEdits.length) { const canShowWidget = editor.getOption(EditorOption.pasteAs).showPasteSelector === 'afterPaste'; return this._postPasteWidgetManager.applyEditAndShowIfNeeded(selections, { activeEditIndex: 0, allEdits: providerEdits }, canShowWidget, tokenSource.token); From 37c4f18caf92131d1015aa95c19a7ae148605806 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Sep 2023 11:34:58 -0700 Subject: [PATCH 513/607] Pick up latest TS to build VS Code (#192219) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 2814401b7f9..938b08591f5 100644 --- a/package.json +++ b/package.json @@ -212,7 +212,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.3.0-dev.20230824", + "typescript": "^5.3.0-dev.20230905", "typescript-formatter": "7.1.0", "underscore": "^1.12.1", "util": "^0.12.4", diff --git a/yarn.lock b/yarn.lock index c49176e5cb6..171f2e83601 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10087,10 +10087,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.3.0-dev.20230824: - version "5.3.0-dev.20230824" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.0-dev.20230824.tgz#14fc65c14c588363c0d290dbbbda8ae0968fd95b" - integrity sha512-iiUWxGibzrRHEBLDJfVymsvpPKflf3cMrw0oQTMQoguFS2ikNlVlfQWAsYeHqGpRQc77nSQkzsE9rAHNHqvIjw== +typescript@^5.3.0-dev.20230905: + version "5.3.0-dev.20230905" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.0-dev.20230905.tgz#b88de602ef4afcc3a80a9c38023df82b7529e42a" + integrity sha512-Nl9MoKWN0YYlCvQnw850L4ZgqdmqwVGCi9cAoQDw4PsqRGaWAi9HKizS9xu0q4qgKKsEKetWCZHT8dBtJTGaMg== typical@^4.0.0: version "4.0.0" From 2ab25fade7ef55492a654389f626f613c835ad5e Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 5 Sep 2023 12:00:53 -0700 Subject: [PATCH 514/607] Match on description as well (#192227) Fixes https://github.com/microsoft/vscode/issues/192077 --- .../contrib/localization/common/localizationsActions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/localization/common/localizationsActions.ts b/src/vs/workbench/contrib/localization/common/localizationsActions.ts index 1cfb6dd5fa2..4e4f83ea12a 100644 --- a/src/vs/workbench/contrib/localization/common/localizationsActions.ts +++ b/src/vs/workbench/contrib/localization/common/localizationsActions.ts @@ -36,6 +36,7 @@ export class ConfigureDisplayLanguageAction extends Action2 { const installedLanguages = await languagePackService.getInstalledLanguages(); const qp = quickInputService.createQuickPick(); + qp.matchOnDescription = true; qp.placeholder = localize('chooseLocale', "Select Display Language"); if (installedLanguages?.length) { From a18893966caf1546eb61ba3a8d522df057ce39d9 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Tue, 5 Sep 2023 15:26:18 -0400 Subject: [PATCH 515/607] use shift for linux --- .../browser/terminal.accessibility.contribution.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 682d14ccde1..b0596b2d2b0 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -128,6 +128,10 @@ registerTerminalAction({ { primary: KeyMod.Alt | KeyCode.F2, secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow], + linux: { + primary: KeyMod.Alt | KeyCode.F2 | KeyMod.Shift, + secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow] + }, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and(CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.focus, ContextKeyExpr.or(terminalTabFocusModeContextKey, TerminalContextKeys.accessibleBufferFocus.negate())) } From 23616317537bd3bd5e8d0849153f78373b4ee037 Mon Sep 17 00:00:00 2001 From: Johannes Date: Tue, 5 Sep 2023 21:33:52 +0200 Subject: [PATCH 516/607] some `ensureNoDisposablesAreLeakedInTestSuite` work --- .../browser/ui/scrollbar/scrollableElement.ts | 4 +- .../documentSymbols/browser/outlineModel.ts | 1 + .../test/browser/outlineModel.test.ts | 9 +- .../test/browser/snippetController2.test.ts | 93 ++++++++++--------- .../editor/contrib/suggest/browser/suggest.ts | 3 +- .../contrib/suggest/browser/suggestWidget.ts | 12 ++- .../suggest/browser/suggestWidgetStatus.ts | 2 + .../test/browser/suggestController.test.ts | 6 +- .../suggest/test/browser/suggestModel.test.ts | 13 ++- .../suggest/test/browser/wordDistance.test.ts | 3 + .../actions/test/common/menuService.test.ts | 13 ++- 11 files changed, 96 insertions(+), 63 deletions(-) diff --git a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts index 45a3b751e41..4073f4272d0 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollableElement.ts @@ -626,14 +626,14 @@ export class DomScrollableElement extends AbstractScrollableElement { super(element, options, scrollable); this._register(scrollable); this._element = element; - this.onScroll((e) => { + this._register(this.onScroll((e) => { if (e.scrollTopChanged) { this._element.scrollTop = e.scrollTop; } if (e.scrollLeftChanged) { this._element.scrollLeft = e.scrollLeft; } - }); + })); this.scanDomNode(); } diff --git a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts index e9746fec571..61caa1c30fa 100644 --- a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts @@ -235,6 +235,7 @@ export class OutlineModel extends TreeElement { } }).finally(() => { listener.dispose(); + cts.dispose(); }); } 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 2249c107004..194b8ee4f16 100644 --- a/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts @@ -18,6 +18,7 @@ import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { OutlineElement, OutlineGroup, OutlineModel, OutlineModelService } from '../../browser/outlineModel'; import { mock } from 'vs/base/test/common/mock'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('OutlineModel', function () { @@ -28,6 +29,8 @@ suite('OutlineModel', function () { disposables.clear(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('OutlineModel#create, cached', async function () { const insta = createModelServices(disposables); @@ -61,6 +64,7 @@ suite('OutlineModel', function () { reg.dispose(); model.dispose(); + service.dispose(); }); test('OutlineModel#create, cached/cancel', async function () { @@ -78,9 +82,10 @@ suite('OutlineModel', function () { const reg = languageFeaturesService.documentSymbolProvider.register({ pattern: '**/path.foo' }, { provideDocumentSymbols(d, token) { return new Promise(resolve => { - token.onCancellationRequested(_ => { + const l = token.onCancellationRequested(_ => { isCancelled = true; resolve(null); + l.dispose(); }); }); } @@ -100,6 +105,8 @@ suite('OutlineModel', function () { reg.dispose(); model.dispose(); + service.dispose(); + }); function fakeSymbolInformation(range: Range, name: string = 'foo'): DocumentSymbol { diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts index 3823efda632..1c9c3a80cdb 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts @@ -22,6 +22,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { EndOfLineSequence } from 'vs/editor/common/model'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('SnippetController2', function () { @@ -34,7 +35,6 @@ suite('SnippetController2', function () { assert.strictEqual(s.length, 0); } - /** @deprecated */ function assertContextKeys(service: MockContextKeyService, inSnippet: boolean, hasPrev: boolean, hasNext: boolean): void { const state = getContextState(service); assert.strictEqual(state.inSnippet, inSnippet, `inSnippetMode`); @@ -50,6 +50,7 @@ suite('SnippetController2', function () { }; } + let ctrl: SnippetController2; let editor: ICodeEditor; let model: TextModel; let contextKeys: MockContextKeyService; @@ -76,16 +77,18 @@ suite('SnippetController2', function () { teardown(function () { model.dispose(); - }); - - test('creation', () => { - const ctrl = instaService.createInstance(SnippetController2, editor); - assertContextKeys(contextKeys, false, false, false); ctrl.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + + test('creation', () => { + ctrl = instaService.createInstance(SnippetController2, editor); + assertContextKeys(contextKeys, false, false, false); + }); + test('insert, insert -> abort', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assertContextKeys(contextKeys, true, false, true); @@ -97,7 +100,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> tab, tab, done', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:one}${2:two}$0'); assertContextKeys(contextKeys, true, false, true); @@ -115,7 +118,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> cursor moves out (left/right)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assertContextKeys(contextKeys, true, false, true); @@ -127,7 +130,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> cursor moves out (up/down)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assertContextKeys(contextKeys, true, false, true); @@ -139,7 +142,7 @@ suite('SnippetController2', function () { }); test('insert, insert -> cursors collapse', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foo${1:bar}foo$0'); assert.strictEqual(SnippetController2.InSnippetMode.getValue(contextKeys), true); @@ -151,7 +154,7 @@ suite('SnippetController2', function () { }); test('insert, insert plain text -> no snippet mode', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('foobar'); assertContextKeys(contextKeys, false, false, false); @@ -159,7 +162,7 @@ suite('SnippetController2', function () { }); test('insert, delete snippet text', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:foobar}$0'); assertContextKeys(contextKeys, true, false, true); @@ -183,7 +186,7 @@ suite('SnippetController2', function () { }); test('insert, nested trivial snippet', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:foo}bar$0'); assertContextKeys(contextKeys, true, false, true); assertSelections(editor, new Selection(1, 1, 1, 4), new Selection(2, 5, 2, 8)); @@ -198,7 +201,7 @@ suite('SnippetController2', function () { }); test('insert, nested snippet', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:foobar}$0'); assertContextKeys(contextKeys, true, false, true); assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); @@ -217,7 +220,7 @@ suite('SnippetController2', function () { }); test('insert, nested plain text', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('${1:foobar}$0'); assertContextKeys(contextKeys, true, false, true); assertSelections(editor, new Selection(1, 1, 1, 7), new Selection(2, 5, 2, 11)); @@ -232,7 +235,7 @@ suite('SnippetController2', function () { }); test('Nested snippets without final placeholder jumps to next outer placeholder, #27898', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('for(const ${1:element} of ${2:array}) {$0}'); assertContextKeys(contextKeys, true, false, true); @@ -251,7 +254,7 @@ suite('SnippetController2', function () { }); test('Inconsistent tab stop behaviour with recursive snippets and tab / shift tab, #27543', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.insert('1_calize(${1:nl}, \'${2:value}\')$0'); assertContextKeys(contextKeys, true, false, true); @@ -275,7 +278,7 @@ suite('SnippetController2', function () { }); test('Snippet tabstop selecting content of previously entered variable only works when separated by space, #23728', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -293,7 +296,7 @@ suite('SnippetController2', function () { }); test('HTML Snippets Combine, #32211', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: false, tabSize: 4, trimAutoWhitespace: false }); @@ -324,7 +327,7 @@ suite('SnippetController2', function () { }); test('Problems with nested snippet insertion #39594', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -341,7 +344,7 @@ suite('SnippetController2', function () { test('Problems with nested snippet insertion #39594 (part2)', function () { // ensure selection-change-to-cancel logic isn't too aggressive - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue('a-\naaa-'); editor.setSelections([new Selection(2, 5, 2, 5), new Selection(1, 3, 1, 3)]); @@ -353,7 +356,7 @@ suite('SnippetController2', function () { test('“Nested” snippets terminating abruptly in VSCode 1.19.2. #42012', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); ctrl.insert('var ${2:${1:name}} = ${1:name} + 1;${0}'); @@ -367,7 +370,7 @@ suite('SnippetController2', function () { test('Placeholders order #58267', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); ctrl.insert('\\pth{$1}$0'); @@ -396,7 +399,7 @@ suite('SnippetController2', function () { }); test('Must tab through deleted tab stops in snippets #31619', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); ctrl.insert('foo${1:a${2:bar}baz}end$0'); @@ -411,7 +414,7 @@ suite('SnippetController2', function () { }); test('Cancelling snippet mode should discard added cursors #68512 (soft cancel)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -431,7 +434,7 @@ suite('SnippetController2', function () { }); test('Cancelling snippet mode should discard added cursors #68512 (hard cancel)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -451,7 +454,7 @@ suite('SnippetController2', function () { }); test('User defined snippet tab stops ignored #72862', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -460,7 +463,7 @@ suite('SnippetController2', function () { }); test('Optional tabstop in snippets #72358', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); editor.setSelection(new Selection(1, 1, 1, 1)); @@ -478,7 +481,7 @@ suite('SnippetController2', function () { }); test('issue #90135: confusing trim whitespace edits', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); @@ -487,7 +490,7 @@ suite('SnippetController2', function () { }); test('issue #145727: insertSnippet can put snippet selections in wrong positions (1 of 2)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); @@ -496,7 +499,7 @@ suite('SnippetController2', function () { }); test('issue #145727: insertSnippet can put snippet selections in wrong positions (2 of 2)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); CoreEditingCommands.Tab.runEditorCommand(null, editor, null); @@ -506,7 +509,7 @@ suite('SnippetController2', function () { }); test('leading TAB by snippets won\'t replace by spaces #101870', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('\tHello World\n\tNew Line'); @@ -514,7 +517,7 @@ suite('SnippetController2', function () { }); test('leading TAB by snippets won\'t replace by spaces #101870 (part 2)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('\tHello World\n\tNew Line\n${1:\tmore}'); @@ -525,7 +528,7 @@ suite('SnippetController2', function () { { // HAPPY - no nested snippet - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('$1\n\n${1/([A-Za-z0-9]+): ([A-Za-z]+).*/$1: \'$2\',/gm}'); @@ -536,7 +539,7 @@ suite('SnippetController2', function () { assert.strictEqual(model.getValue(), `foo: number;\n\nfoo: 'number',`); } - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.updateOptions({ insertSpaces: true, tabSize: 4 }); ctrl.insert('$1\n\n${1/([A-Za-z0-9]+): ([A-Za-z]+).*/$1: \'$2\',/gm}'); @@ -553,7 +556,7 @@ suite('SnippetController2', function () { test('apply, tab, done', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue('foo("bar")'); @@ -575,7 +578,7 @@ suite('SnippetController2', function () { model.setValue('foo("bar")'); - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.apply([ { range: new Range(1, 5, 1, 10), template: '$1' }, { range: new Range(1, 1, 1, 1), template: 'const ${1:new_const}$0 = "bar";\n' } @@ -594,7 +597,7 @@ suite('SnippetController2', function () { model.setValue('foo\nbar'); - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.apply([ { range: new Range(1, 4, 1, 4), template: '${3}' }, { range: new Range(2, 4, 2, 4), template: '$3' }, @@ -616,7 +619,7 @@ suite('SnippetController2', function () { test('nested into apply works', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue('onetwo'); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); @@ -647,7 +650,7 @@ suite('SnippetController2', function () { test('nested into insert abort "outer" snippet', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue('one\ntwo'); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); @@ -668,7 +671,7 @@ suite('SnippetController2', function () { test('nested into "insert" abort "outer" snippet (2)', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue('one\ntwo'); editor.setSelections([new Selection(1, 1, 1, 1), new Selection(2, 1, 2, 1)]); @@ -700,7 +703,7 @@ suite('SnippetController2', function () { test('Bug: cursor position $0 with user snippets #163808', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); ctrl.insert('\n \n$0"\n'); @@ -713,7 +716,7 @@ suite('SnippetController2', function () { }); test('EOL-Sequence (CRLF) shifts tab stop in isFileTemplate snippets #167386', function () { - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); model.setValue(''); model.setEOL(EndOfLineSequence.CRLF); @@ -730,7 +733,7 @@ suite('SnippetController2', function () { model.setValue('function foo(f, x, condition) {\n f();\n return x;\n}'); const sel = new Range(2, 5, 3, 14); editor.setSelection(sel); - const ctrl = instaService.createInstance(SnippetController2, editor); + ctrl = instaService.createInstance(SnippetController2, editor); ctrl.apply([{ range: sel, template: 'if (${1:condition}) {\n\t$TM_SELECTED_TEXT$0\n}' diff --git a/src/vs/editor/contrib/suggest/browser/suggest.ts b/src/vs/editor/contrib/suggest/browser/suggest.ts index 9e8fbaee192..0ca6b0e09d0 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.ts +++ b/src/vs/editor/contrib/suggest/browser/suggest.ts @@ -146,7 +146,6 @@ export class CompletionItem { this._resolveCache = Promise.resolve(this.provider.resolveCompletionItem!(this.completion, token)).then(value => { Object.assign(this.completion, value); this._resolveDuration = sw.elapsed(); - sub.dispose(); }, err => { if (isCancellationError(err)) { // the IPC queue will reject the request with the @@ -154,6 +153,8 @@ export class CompletionItem { this._resolveCache = undefined; this._resolveDuration = undefined; } + }).finally(() => { + sub.dispose(); }); } return this._resolveCache; diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 09ebd2bdda0..6a03536715e 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -209,7 +209,7 @@ export class SuggestWidget implements IDisposable { this._messageElement = dom.append(this.element.domNode, dom.$('.message')); this._listElement = dom.append(this.element.domNode, dom.$('.tree')); - const details = instantiationService.createInstance(SuggestDetailsWidget, this.editor); + const details = this._disposables.add(instantiationService.createInstance(SuggestDetailsWidget, this.editor)); details.onDidClose(this.toggleDetails, this, this._disposables); this._details = new SuggestDetailsOverlay(details, this.editor); @@ -399,10 +399,12 @@ export class SuggestWidget implements IDisposable { } }, 250); const sub = token.onCancellationRequested(() => loading.dispose()); - const result = await item.resolve(token); - loading.dispose(); - sub.dispose(); - return result; + try { + return await item.resolve(token); + } finally { + loading.dispose(); + sub.dispose(); + } }); this._currentSuggestionDetails.then(() => { diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidgetStatus.ts b/src/vs/editor/contrib/suggest/browser/suggestWidgetStatus.ts index 5b843a83563..25d19c054d2 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidgetStatus.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidgetStatus.ts @@ -60,6 +60,8 @@ export class SuggestWidgetStatus { dispose(): void { this._menuDisposables.dispose(); + this._leftActions.dispose(); + this._rightActions.dispose(); this.element.remove(); } diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts index 4e8202abc64..f24030cb93e 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts @@ -43,18 +43,20 @@ suite('SuggestController', function () { let model: TextModel; const languageFeaturesService = new LanguageFeaturesService(); - teardown(function () { + disposables.clear(); }); + // ensureNoDisposablesAreLeakedInTestSuite(); + setup(function () { const serviceCollection = new ServiceCollection( [ILanguageFeaturesService, languageFeaturesService], [ITelemetryService, NullTelemetryService], [ILogService, new NullLogService()], - [IStorageService, new InMemoryStorageService()], + [IStorageService, disposables.add(new InMemoryStorageService())], [IKeybindingService, new MockKeybindingService()], [IEditorWorkerService, new class extends mock() { override computeWordRanges() { 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 7d2b9f51bb8..2bbfe524bfb 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts @@ -40,15 +40,17 @@ import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeat import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { getSnippetSuggestSupport, setSnippetSuggestSupport } from 'vs/editor/contrib/suggest/browser/suggest'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; function createMockEditor(model: TextModel, languageFeaturesService: ILanguageFeaturesService): ITestCodeEditor { + const storeService = new InMemoryStorageService(); const editor = createTestCodeEditor(model, { serviceCollection: new ServiceCollection( [ILanguageFeaturesService, languageFeaturesService], [ITelemetryService, NullTelemetryService], - [IStorageService, new InMemoryStorageService()], + [IStorageService, storeService], [IKeybindingService, new MockKeybindingService()], [ISuggestMemoryService, new class implements ISuggestMemoryService { declare readonly _serviceBrand: undefined; @@ -66,8 +68,11 @@ function createMockEditor(model: TextModel, languageFeaturesService: ILanguageFe }], ), }); - editor.registerAndInstantiateContribution(SnippetController2.ID, SnippetController2); + const ctrl = editor.registerAndInstantiateContribution(SnippetController2.ID, SnippetController2); editor.hasWidgetFocus = () => true; + + editor.registerDisposable(ctrl); + editor.registerDisposable(storeService); return editor; } @@ -141,6 +146,8 @@ suite('SuggestModel - Context', function () { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('Context - shouldAutoTrigger', function () { const model = createTextModel('Das Pferd frisst keinen Gurkensalat - Philipp Reis 1861.\nWer hat\'s erfunden?'); disposables.add(model); @@ -220,6 +227,8 @@ suite('SuggestModel - TriggerAndCancelOracle', function () { disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + function withOracle(callback: (model: SuggestModel, editor: ITestCodeEditor) => any): Promise { return new Promise((resolve, reject) => { diff --git a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts index 72a287f12a0..04f7bce1cc1 100644 --- a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts @@ -26,6 +26,7 @@ import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/te import { NullLogService } from 'vs/platform/log/common/log'; import { LanguageFeaturesService } from 'vs/editor/common/services/languageFeaturesService'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('suggest, word distance', function () { @@ -88,6 +89,8 @@ suite('suggest, word distance', function () { disposables.clear(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createSuggestItem(label: string, overwriteBefore: number, position: IPosition): CompletionItem { const suggestion: languages.CompletionItem = { label, diff --git a/src/vs/platform/actions/test/common/menuService.test.ts b/src/vs/platform/actions/test/common/menuService.test.ts index caf3ad95463..70b864c76bd 100644 --- a/src/vs/platform/actions/test/common/menuService.test.ts +++ b/src/vs/platform/actions/test/common/menuService.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { isIMenuItem, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { MenuService } from 'vs/platform/actions/common/menuService'; import { NullCommandService } from 'vs/platform/commands/test/common/nullCommandService'; @@ -38,6 +39,8 @@ suite('MenuService', function () { disposables.clear(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('group sorting', function () { disposables.add(MenuRegistry.appendMenuItem(testMenuId, { @@ -65,7 +68,7 @@ suite('MenuService', function () { group: 'navigation' })); - const groups = menuService.createMenu(testMenuId, contextKeyService).getActions(); + const groups = disposables.add(menuService.createMenu(testMenuId, contextKeyService)).getActions(); assert.strictEqual(groups.length, 5); const [one, two, three, four, five] = groups; @@ -94,7 +97,7 @@ suite('MenuService', function () { group: 'Hello' })); - const groups = menuService.createMenu(testMenuId, contextKeyService).getActions(); + const groups = disposables.add(menuService.createMenu(testMenuId, contextKeyService)).getActions(); assert.strictEqual(groups.length, 1); const [, actions] = groups[0]; @@ -131,7 +134,7 @@ suite('MenuService', function () { order: -1 })); - const groups = menuService.createMenu(testMenuId, contextKeyService).getActions(); + const groups = disposables.add(menuService.createMenu(testMenuId, contextKeyService)).getActions(); assert.strictEqual(groups.length, 1); const [, actions] = groups[0]; @@ -165,7 +168,7 @@ suite('MenuService', function () { order: 1.1 })); - const groups = menuService.createMenu(testMenuId, contextKeyService).getActions(); + const groups = disposables.add(menuService.createMenu(testMenuId, contextKeyService)).getActions(); assert.strictEqual(groups.length, 1); const [[, actions]] = groups; @@ -183,7 +186,7 @@ suite('MenuService', function () { command: { id: 'a', title: 'Explicit' } })); - MenuRegistry.addCommand({ id: 'b', title: 'Implicit' }); + disposables.add(MenuRegistry.addCommand({ id: 'b', title: 'Implicit' })); let foundA = false; let foundB = false; From eb29e875218ad01037b8f42c120e2d4402b5a1bd Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Tue, 5 Sep 2023 15:04:35 -0700 Subject: [PATCH 517/607] Add ensureNoDisposablesAreLeakedInTestSuite calls (#192228) --- .../workbench/contrib/emmet/test/browser/emmetAction.test.ts | 4 +++- .../test/browser/keybindingsEditorContribution.test.ts | 3 +++ .../preferences/test/browser/settingsTreeModels.test.ts | 3 +++ .../preferences/test/common/smartSnippetInserter.test.ts | 2 ++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts index da5770a1d73..f3a0b28bf04 100644 --- a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts +++ b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts @@ -8,6 +8,7 @@ import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class MockGrammarContributions implements IGrammarContributions { private scopeName: string; @@ -22,7 +23,6 @@ class MockGrammarContributions implements IGrammarContributions { } suite('Emmet', () => { - test('Get language mode and parent mode for emmet', () => { withTestCodeEditor([], {}, (editor, viewModel, instantiationService) => { const languageService = instantiationService.get(ILanguageService); @@ -63,4 +63,6 @@ suite('Emmet', () => { }); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/preferences/test/browser/keybindingsEditorContribution.test.ts b/src/vs/workbench/contrib/preferences/test/browser/keybindingsEditorContribution.test.ts index db253bf5783..e5b24e6bd3b 100644 --- a/src/vs/workbench/contrib/preferences/test/browser/keybindingsEditorContribution.test.ts +++ b/src/vs/workbench/contrib/preferences/test/browser/keybindingsEditorContribution.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { KeybindingEditorDecorationsRenderer } from 'vs/workbench/contrib/preferences/browser/keybindingsEditorContribution'; suite('KeybindingsEditorContribution', () => { @@ -37,4 +38,6 @@ suite('KeybindingsEditorContribution', () => { assertEqual('cmd+shift+p', 'shift+cmd+p'); assertEqual('cmd+shift+p', 'shift-cmd-p'); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts b/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts index 8d8d19abc03..6bb2d93fe7d 100644 --- a/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts +++ b/src/vs/workbench/contrib/preferences/test/browser/settingsTreeModels.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { settingKeyToDisplayFormat, parseQuery, IParsedQuery } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; suite('SettingsTree', () => { @@ -327,4 +328,6 @@ suite('SettingsTree', () => { languageFilter: 'cpp' }); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts b/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts index 085f1b548b3..26d0c30d9cb 100644 --- a/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts +++ b/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { SmartSnippetInserter } from 'vs/workbench/contrib/preferences/common/smartSnippetInserter'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { Position } from 'vs/editor/common/core/position'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('SmartSnippetInserter', () => { @@ -159,4 +160,5 @@ suite('SmartSnippetInserter', () => { }); }); + ensureNoDisposablesAreLeakedInTestSuite(); }); From b6bc92b3a9b0cbd522bfabf6fcb9ea619ad8c320 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 5 Sep 2023 15:22:16 -0700 Subject: [PATCH 518/607] testing: make openTesting option for explorer (#192237) Fixes #191272 --- .../testing/browser/testingProgressUiService.ts | 14 +++++++++++--- .../contrib/testing/common/configuration.ts | 9 ++++++--- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts b/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts index 515882e73d7..f916c0df5ab 100644 --- a/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts +++ b/src/vs/workbench/contrib/testing/browser/testingProgressUiService.ts @@ -40,8 +40,12 @@ export class TestingProgressTrigger extends Disposable { return; } + if (cfg === AutoOpenTesting.OpenExplorerOnTestStart) { + return this.openExplorerView(); + } + if (cfg === AutoOpenTesting.OpenOnTestStart) { - return this.openTestView(); + return this.openResultsView(); } // open on failure @@ -49,13 +53,17 @@ export class TestingProgressTrigger extends Disposable { disposable.add(result.onComplete(() => disposable.dispose())); disposable.add(result.onChange(e => { if (e.reason === TestResultItemChangeReason.OwnStateChange && isFailedState(e.item.ownComputedState)) { - this.openTestView(); + this.openResultsView(); disposable.dispose(); } })); } - private openTestView() { + private openExplorerView() { + this.viewsService.openView(Testing.ExplorerViewId, false); + } + + private openResultsView() { this.viewsService.openView(Testing.ResultsViewId, false); } } diff --git a/src/vs/workbench/contrib/testing/common/configuration.ts b/src/vs/workbench/contrib/testing/common/configuration.ts index 1bddb34c627..c5dda270fbf 100644 --- a/src/vs/workbench/contrib/testing/common/configuration.ts +++ b/src/vs/workbench/contrib/testing/common/configuration.ts @@ -25,6 +25,7 @@ export const enum AutoOpenTesting { NeverOpen = 'neverOpen', OpenOnTestStart = 'openOnTestStart', OpenOnTestFailure = 'openOnTestFailure', + OpenExplorerOnTestStart = 'openExplorerOnTestStart', } export const enum AutoOpenPeekViewWhen { @@ -132,11 +133,13 @@ export const testingConfiguration: IConfigurationNode = { AutoOpenTesting.NeverOpen, AutoOpenTesting.OpenOnTestStart, AutoOpenTesting.OpenOnTestFailure, + AutoOpenTesting.OpenExplorerOnTestStart, ], enumDescriptions: [ - localize('testing.openTesting.neverOpen', 'Never automatically open the testing view'), - localize('testing.openTesting.openOnTestStart', 'Open the testing view when tests start'), - localize('testing.openTesting.openOnTestFailure', 'Open the testing view on any test failure'), + localize('testing.openTesting.neverOpen', 'Never automatically open the testing views'), + localize('testing.openTesting.openOnTestStart', 'Open the test results view when tests start'), + localize('testing.openTesting.openOnTestFailure', 'Open the test result view on any test failure'), + localize('testing.openTesting.openExplorerOnTestStart', 'Open the test explorer when tests start'), ], default: 'openOnTestStart', description: localize('testing.openTesting', "Controls when the testing view should open.") From 13fe2caa64627e6037e90e72b6752aeb1e539d79 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 15:28:05 -0700 Subject: [PATCH 519/607] Adopt ensureNoDisposablesAreLeakedInTestSuite in contextKey tests --- .../contextkey/browser/contextKeyService.ts | 32 +++++------ .../test/browser/contextkey.test.ts | 55 ++++++++++--------- 2 files changed, 41 insertions(+), 46 deletions(-) diff --git a/src/vs/platform/contextkey/browser/contextKeyService.ts b/src/vs/platform/contextkey/browser/contextKeyService.ts index fd49a5ff37c..1966c6862f0 100644 --- a/src/vs/platform/contextkey/browser/contextKeyService.ts +++ b/src/vs/platform/contextkey/browser/contextKeyService.ts @@ -5,7 +5,7 @@ import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; -import { DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { MarshalledObject } from 'vs/base/common/marshalling'; import { MarshalledId } from 'vs/base/common/marshallingIds'; import { cloneAndChange, distinct } from 'vs/base/common/objects'; @@ -264,16 +264,17 @@ function allEventKeysInContext(event: IContextKeyChangeEvent, context: Record({ merge: input => new CompositeContextKeyChangeEvent(input) }); + protected _onDidChangeContext = this._register(new PauseableEmitter({ merge: input => new CompositeContextKeyChangeEvent(input) })); readonly onDidChangeContext = this._onDidChangeContext.event; constructor(myContextId: number) { + super(); this._isDisposed = false; this._myContextId = myContextId; } @@ -365,6 +366,11 @@ export abstract class AbstractContextKeyService implements IContextKeyService { public abstract createChildContext(parentContextId?: number): number; public abstract disposeContext(contextId: number): void; public abstract updateParent(parentContextKeyService?: IContextKeyService): void; + + public override dispose(): void { + super.dispose(); + this._isDisposed = true; + } } export class ContextKeyService extends AbstractContextKeyService implements IContextKeyService { @@ -372,16 +378,12 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon private _lastContextId: number; private readonly _contexts = new Map(); - private readonly _toDispose = new DisposableStore(); - constructor(@IConfigurationService configurationService: IConfigurationService) { super(0); this._lastContextId = 0; - - const myContext = new ConfigAwareContextValuesContainer(this._myContextId, configurationService, this._onDidChangeContext); + const myContext = this._register(new ConfigAwareContextValuesContainer(this._myContextId, configurationService, this._onDidChangeContext)); this._contexts.set(this._myContextId, myContext); - this._toDispose.add(myContext); // Uncomment this to see the contexts continuously logged // let lastLoggedValue: string | null = null; @@ -395,12 +397,6 @@ export class ContextKeyService extends AbstractContextKeyService implements ICon // }, 2000); } - public dispose(): void { - this._onDidChangeContext.dispose(); - this._isDisposed = true; - this._toDispose.dispose(); - } - public getContextValuesContainer(contextId: number): Context { if (this._isDisposed) { return NullContext.INSTANCE; @@ -433,7 +429,7 @@ class ScopedContextKeyService extends AbstractContextKeyService { private _parent: AbstractContextKeyService; private _domNode: IContextKeyServiceTarget; - private readonly _parentChangeListener = new MutableDisposable(); + private readonly _parentChangeListener = this._register(new MutableDisposable()); constructor(parent: AbstractContextKeyService, domNode: IContextKeyServiceTarget) { super(parent.createChildContext()); @@ -464,16 +460,14 @@ class ScopedContextKeyService extends AbstractContextKeyService { }); } - public dispose(): void { + public override dispose(): void { if (this._isDisposed) { return; } - this._onDidChangeContext.dispose(); this._parent.disposeContext(this._myContextId); - this._parentChangeListener.dispose(); this._domNode.removeAttribute(KEYBINDING_CONTEXT_ATTR); - this._isDisposed = true; + super.dispose(); } public getContextValuesContainer(contextId: number): Context { diff --git a/src/vs/platform/contextkey/test/browser/contextkey.test.ts b/src/vs/platform/contextkey/test/browser/contextkey.test.ts index 429f76d42c4..b56d4b874da 100644 --- a/src/vs/platform/contextkey/test/browser/contextkey.test.ts +++ b/src/vs/platform/contextkey/test/browser/contextkey.test.ts @@ -2,11 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { DeferredPromise } from 'vs/base/common/async'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ContextKeyService, setContext } from 'vs/platform/contextkey/browser/contextKeyService'; @@ -16,12 +17,14 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; suite('ContextKeyService', () => { - test('updateParent', () => { - const root = new ContextKeyService(new TestConfigurationService()); - const parent1 = root.createScoped(document.createElement('div')); - const parent2 = root.createScoped(document.createElement('div')); + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); - const child = parent1.createScoped(document.createElement('div')); + test('updateParent', () => { + const root = testDisposables.add(new ContextKeyService(new TestConfigurationService())); + const parent1 = testDisposables.add(root.createScoped(document.createElement('div'))); + const parent2 = testDisposables.add(root.createScoped(document.createElement('div'))); + + const child = testDisposables.add(parent1.createScoped(document.createElement('div'))); parent1.createKey('testA', 1); parent1.createKey('testB', 2); parent1.createKey('testD', 0); @@ -36,7 +39,7 @@ suite('ContextKeyService', () => { complete = _complete; reject = _reject; }); - child.onDidChangeContext(e => { + testDisposables.add(child.onDidChangeContext(e => { try { assert.ok(e.affectsSome(new Set(['testA'])), 'testA changed'); assert.ok(e.affectsSome(new Set(['testB'])), 'testB changed'); @@ -53,7 +56,7 @@ suite('ContextKeyService', () => { } complete(); - }); + })); child.updateParent(parent2); @@ -61,18 +64,18 @@ suite('ContextKeyService', () => { }); test('updateParent to same service', () => { - const root = new ContextKeyService(new TestConfigurationService()); - const parent1 = root.createScoped(document.createElement('div')); + const root = testDisposables.add(new ContextKeyService(new TestConfigurationService())); + const parent1 = testDisposables.add(root.createScoped(document.createElement('div'))); - const child = parent1.createScoped(document.createElement('div')); + const child = testDisposables.add(parent1.createScoped(document.createElement('div'))); parent1.createKey('testA', 1); parent1.createKey('testB', 2); parent1.createKey('testD', 0); let eventFired = false; - child.onDidChangeContext(e => { + testDisposables.add(child.onDidChangeContext(e => { eventFired = true; - }); + })); child.updateParent(parent1); @@ -80,10 +83,9 @@ suite('ContextKeyService', () => { }); test('issue #147732: URIs as context values', () => { - const disposables = new DisposableStore(); const configurationService: IConfigurationService = new TestConfigurationService(); - const contextKeyService: IContextKeyService = disposables.add(new ContextKeyService(configurationService)); - const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection( + const contextKeyService: IContextKeyService = testDisposables.add(new ContextKeyService(configurationService)); + const instantiationService = testDisposables.add(new TestInstantiationService(new ServiceCollection( [IConfigurationService, configurationService], [IContextKeyService, contextKeyService], [ITelemetryService, new class extends mock() { @@ -99,26 +101,25 @@ suite('ContextKeyService', () => { const expr = ContextKeyExpr.in('notebookCellResource', 'jupyter.runByLineCells'); assert.deepStrictEqual(contextKeyService.contextMatchesRules(expr), true); - disposables.dispose(); }); test('suppress update event from parent when one key is overridden by child', () => { - const root = new ContextKeyService(new TestConfigurationService()); - const child = root.createScoped(document.createElement('div')); + const root = testDisposables.add(new ContextKeyService(new TestConfigurationService())); + const child = testDisposables.add(root.createScoped(document.createElement('div'))); root.createKey('testA', 1); child.createKey('testA', 4); let fired = false; - const event = child.onDidChangeContext(e => fired = true); + const event = testDisposables.add(child.onDidChangeContext(e => fired = true)); root.setContext('testA', 10); assert.strictEqual(fired, false, 'Should not fire event when overridden key is updated in parent'); event.dispose(); }); test('suppress update event from parent when all keys are overridden by child', () => { - const root = new ContextKeyService(new TestConfigurationService()); - const child = root.createScoped(document.createElement('div')); + const root = testDisposables.add(new ContextKeyService(new TestConfigurationService())); + const child = testDisposables.add(root.createScoped(document.createElement('div'))); root.createKey('testA', 1); root.createKey('testB', 2); @@ -129,7 +130,7 @@ suite('ContextKeyService', () => { child.createKey('testD', 6); let fired = false; - const event = child.onDidChangeContext(e => fired = true); + const event = testDisposables.add(child.onDidChangeContext(e => fired = true)); root.bufferChangeEvents(() => { root.setContext('testA', 10); root.setContext('testB', 20); @@ -141,8 +142,8 @@ suite('ContextKeyService', () => { }); test('pass through update event from parent when one key is not overridden by child', () => { - const root = new ContextKeyService(new TestConfigurationService()); - const child = root.createScoped(document.createElement('div')); + const root = testDisposables.add(new ContextKeyService(new TestConfigurationService())); + const child = testDisposables.add(root.createScoped(document.createElement('div'))); root.createKey('testA', 1); root.createKey('testB', 2); @@ -153,7 +154,7 @@ suite('ContextKeyService', () => { child.createKey('testD', 6); const def = new DeferredPromise(); - child.onDidChangeContext(e => { + testDisposables.add(child.onDidChangeContext(e => { try { assert.ok(e.affectsSome(new Set(['testA'])), 'testA changed'); assert.ok(e.affectsSome(new Set(['testB'])), 'testB changed'); @@ -164,7 +165,7 @@ suite('ContextKeyService', () => { } def.complete(undefined); - }); + })); root.bufferChangeEvents(() => { root.setContext('testA', 10); From fc2d092a344eb133abf7ab64c8bd8ffe1106d55b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 15:32:56 -0700 Subject: [PATCH 520/607] Adopt ensureNoDisposablesAreLeakedInTestSuite for MainThreadWorkspace tests --- .../test/browser/mainThreadWorkspace.test.ts | 27 ++++++++----------- .../test/browser/workbenchTestServices.ts | 2 +- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts b/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts index a13928a488a..7000d9edb5b 100644 --- a/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadWorkspace.test.ts @@ -3,35 +3,30 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { ISearchService, IFileQuery } from 'vs/workbench/services/search/common/search'; -import { MainThreadWorkspace } from 'vs/workbench/api/browser/mainThreadWorkspace'; import * as assert from 'assert'; -import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; +import { MainThreadWorkspace } from 'vs/workbench/api/browser/mainThreadWorkspace'; +import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; +import { IFileQuery, ISearchService } from 'vs/workbench/services/search/common/search'; +import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; suite('MainThreadWorkspace', () => { + const disposables = ensureNoDisposablesAreLeakedInTestSuite(); - let disposables: DisposableStore; let configService: TestConfigurationService; let instantiationService: TestInstantiationService; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables) as TestInstantiationService; configService = instantiationService.get(IConfigurationService) as TestConfigurationService; configService.setUserConfiguration('search', {}); }); - teardown(() => { - disposables.dispose(); - }); - test('simple', () => { instantiationService.stub(ISearchService, { fileSearch(query: IFileQuery) { @@ -45,7 +40,7 @@ suite('MainThreadWorkspace', () => { } }); - const mtw = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); return mtw.$startFileSearch('foo', null, null, 10, new CancellationTokenSource().token); }); @@ -67,7 +62,7 @@ suite('MainThreadWorkspace', () => { } }); - const mtw = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); return mtw.$startFileSearch('', null, null, 10, new CancellationTokenSource().token); }); @@ -88,7 +83,7 @@ suite('MainThreadWorkspace', () => { } }); - const mtw = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); return mtw.$startFileSearch('', null, false, 10, new CancellationTokenSource().token); }); @@ -102,7 +97,7 @@ suite('MainThreadWorkspace', () => { } }); - const mtw = instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } })); + const mtw = disposables.add(instantiationService.createInstance(MainThreadWorkspace, SingleProxyRPCProtocol({ $initializeWorkspace: () => { } }))); return mtw.$startFileSearch('', null, 'exclude/**', 10, new CancellationTokenSource().token); }); }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index ed87b23c2ba..1340c38c2de 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -241,7 +241,7 @@ export function workbenchInstantiationService( contextKeyService?: (instantiationService: IInstantiationService) => IContextKeyService; textEditorService?: (instantiationService: IInstantiationService) => ITextEditorService; }, - disposables: DisposableStore = new DisposableStore() + disposables: Pick = new DisposableStore() ): TestInstantiationService { const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()]))); From 449dfbcbc06856542c281dfdd02bbc6b6e520e05 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 15:48:52 -0700 Subject: [PATCH 521/607] Adopt ensureNoDisposablesAreLeakedInTestSuite in ExtHostSearch test --- src/vs/workbench/api/node/extHostSearch.ts | 16 ++++++---- .../api/test/node/extHostSearch.test.ts | 30 +++++++++---------- .../search/common/fileSearchManager.ts | 4 ++- .../search/common/textSearchManager.ts | 4 ++- 4 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 8e1dab9ef1b..ef4f2b68e69 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; @@ -20,7 +20,7 @@ import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUt import { NativeTextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; import type * as vscode from 'vscode'; -export class NativeExtHostSearch extends ExtHostSearch { +export class NativeExtHostSearch extends ExtHostSearch implements IDisposable { protected _pfs: typeof pfs = pfs; // allow extending for tests @@ -29,6 +29,8 @@ export class NativeExtHostSearch extends ExtHostSearch { private _registeredEHSearchProvider = false; + private _disposables = new DisposableStore(); + constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, @@ -38,12 +40,16 @@ export class NativeExtHostSearch extends ExtHostSearch { super(extHostRpc, _uriTransformer, _logService); const outputChannel = new OutputChannel('RipgrepSearchUD', this._logService); - this.registerTextSearchProvider(Schemas.vscodeUserData, new RipgrepSearchProvider(outputChannel)); + this._disposables.add(this.registerTextSearchProvider(Schemas.vscodeUserData, new RipgrepSearchProvider(outputChannel))); if (initData.remote.isRemote && initData.remote.authority) { this._registerEHSearchProviders(); } } + dispose(): void { + this._disposables.dispose(); + } + override $enableExtensionHostSearch(): void { this._registerEHSearchProviders(); } @@ -55,8 +61,8 @@ export class NativeExtHostSearch extends ExtHostSearch { this._registeredEHSearchProvider = true; const outputChannel = new OutputChannel('RipgrepSearchEH', this._logService); - this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel)); - this.registerInternalFileSearchProvider(Schemas.file, new SearchService('fileSearchProvider')); + this._disposables.add(this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel))); + this._disposables.add(this.registerInternalFileSearchProvider(Schemas.file, new SearchService('fileSearchProvider'))); } private registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable { diff --git a/src/vs/workbench/api/test/node/extHostSearch.test.ts b/src/vs/workbench/api/test/node/extHostSearch.test.ts index 3ed96b49e9d..7942b81654e 100644 --- a/src/vs/workbench/api/test/node/extHostSearch.test.ts +++ b/src/vs/workbench/api/test/node/extHostSearch.test.ts @@ -8,26 +8,25 @@ import { mapArrayOrNot } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { isCancellationError } from 'vs/base/common/errors'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { MainContext, MainThreadSearchShape } from 'vs/workbench/api/common/extHost.protocol'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { Range } from 'vs/workbench/api/common/extHostTypes'; import { URITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { NativeExtHostSearch } from 'vs/workbench/api/node/extHostSearch'; +import { TestRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { IFileMatch, IFileQuery, IPatternInfo, IRawFileMatch2, ISearchCompleteStats, ISearchQuery, ITextQuery, QueryType, resultIsMatch } from 'vs/workbench/services/search/common/search'; import { TextSearchManager } from 'vs/workbench/services/search/common/textSearchManager'; import { NativeTextSearchManager } from 'vs/workbench/services/search/node/textSearchManager'; -import { TestRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import type * as vscode from 'vscode'; let rpcProtocol: TestRPCProtocol; let extHostSearch: NativeExtHostSearch; -const disposables = new DisposableStore(); let mockMainThreadSearch: MockMainThreadSearch; class MockMainThreadSearch implements MainThreadSearchShape { @@ -68,6 +67,8 @@ function extensionResultIsMatch(data: vscode.TextSearchResult): data is vscode.T } suite('ExtHostSearch', () => { + const disposables = ensureNoDisposablesAreLeakedInTestSuite(); + async function registerTestTextSearchProvider(provider: vscode.TextSearchProvider, scheme = 'file'): Promise { disposables.add(extHostSearch.registerTextSearchProvider(scheme, provider)); await rpcProtocol.sync(); @@ -137,7 +138,7 @@ suite('ExtHostSearch', () => { rpcProtocol.set(MainContext.MainThreadSearch, mockMainThreadSearch); mockPFS = {}; - extHostSearch = new class extends NativeExtHostSearch { + extHostSearch = disposables.add(new class extends NativeExtHostSearch { constructor() { super( rpcProtocol, @@ -151,11 +152,10 @@ suite('ExtHostSearch', () => { protected override createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager { return new NativeTextSearchManager(query, provider, this._pfs); } - }; + }); }); teardown(() => { - disposables.clear(); return rpcProtocol.sync(); }); @@ -231,7 +231,7 @@ suite('ExtHostSearch', () => { if (token.isCancellationRequested) { onCancel(); } else { - token.onCancellationRequested(() => onCancel()); + disposables.add(token.onCancellationRequested(() => onCancel())); } }); } @@ -512,7 +512,7 @@ suite('ExtHostSearch', () => { let wasCanceled = false; await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => wasCanceled = true); + disposables.add(token.onCancellationRequested(() => wasCanceled = true)); return Promise.resolve(reportedResults); } @@ -548,7 +548,7 @@ suite('ExtHostSearch', () => { let wasCanceled = false; await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => wasCanceled = true); + disposables.add(token.onCancellationRequested(() => wasCanceled = true)); return Promise.resolve(reportedResults); } @@ -583,7 +583,7 @@ suite('ExtHostSearch', () => { let wasCanceled = false; await registerTestFileSearchProvider({ provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => wasCanceled = true); + disposables.add(token.onCancellationRequested(() => wasCanceled = true)); return Promise.resolve(reportedResults); } @@ -613,7 +613,7 @@ suite('ExtHostSearch', () => { let cancels = 0; await registerTestFileSearchProvider({ async provideFileSearchResults(query: vscode.FileSearchQuery, options: vscode.FileSearchOptions, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => cancels++); + disposables.add(token.onCancellationRequested(() => cancels++)); // Provice results async so it has a chance to invoke every provider await new Promise(r => process.nextTick(r)); @@ -1083,7 +1083,7 @@ suite('ExtHostSearch', () => { let wasCanceled = false; await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => wasCanceled = true); + disposables.add(token.onCancellationRequested(() => wasCanceled = true)); providedResults.forEach(r => progress.report(r)); return Promise.resolve(null!); } @@ -1116,7 +1116,7 @@ suite('ExtHostSearch', () => { let wasCanceled = false; await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => wasCanceled = true); + disposables.add(token.onCancellationRequested(() => wasCanceled = true)); providedResults.forEach(r => progress.report(r)); return Promise.resolve(null!); } @@ -1148,7 +1148,7 @@ suite('ExtHostSearch', () => { let wasCanceled = false; await registerTestTextSearchProvider({ provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => wasCanceled = true); + disposables.add(token.onCancellationRequested(() => wasCanceled = true)); providedResults.forEach(r => progress.report(r)); return Promise.resolve(null!); } @@ -1205,7 +1205,7 @@ suite('ExtHostSearch', () => { let cancels = 0; await registerTestTextSearchProvider({ async provideTextSearchResults(query: vscode.TextSearchQuery, options: vscode.TextSearchOptions, progress: vscode.Progress, token: vscode.CancellationToken): Promise { - token.onCancellationRequested(() => cancels++); + disposables.add(token.onCancellationRequested(() => cancels++)); await new Promise(r => process.nextTick(r)); [ 'file1.ts', diff --git a/src/vs/workbench/services/search/common/fileSearchManager.ts b/src/vs/workbench/services/search/common/fileSearchManager.ts index 3fb1237e178..960c90492fd 100644 --- a/src/vs/workbench/services/search/common/fileSearchManager.ts +++ b/src/vs/workbench/services/search/common/fileSearchManager.ts @@ -329,7 +329,7 @@ export class FileSearchManager { } private doSearch(engine: FileSearchEngine, batchSize: number, onResultBatch: (matches: IInternalFileMatch[]) => void, token: CancellationToken): Promise { - token.onCancellationRequested(() => { + const listener = token.onCancellationRequested(() => { engine.cancel(); }); @@ -349,12 +349,14 @@ export class FileSearchManager { onResultBatch(batch); } + listener.dispose(); return result; }, error => { if (batch.length) { onResultBatch(batch); } + listener.dispose(); return Promise.reject(error); }); } diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index 68dcacea828..9210f766f2c 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -31,7 +31,7 @@ export class TextSearchManager { search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { const folderQueries = this.query.folderQueries || []; const tokenSource = new CancellationTokenSource(); - token.onCancellationRequested(() => tokenSource.cancel()); + const listener = token.onCancellationRequested(() => tokenSource.cancel()); return new Promise((resolve, reject) => { this.collector = new TextSearchResultsCollector(onProgress); @@ -65,6 +65,7 @@ export class TextSearchManager { return this.searchInFolder(fq, r => onResult(r, i), tokenSource.token); })).then(results => { tokenSource.dispose(); + listener.dispose(); this.collector!.flush(); const someFolderHitLImit = results.some(result => !!result && !!result.limitHit); @@ -81,6 +82,7 @@ export class TextSearchManager { }); }, (err: Error) => { tokenSource.dispose(); + listener.dispose(); const errMsg = toErrorMessage(err); reject(new Error(errMsg)); }); From f73b2315b29b9bcd0e567c7e1cd6d9742511bc1c Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 15:51:12 -0700 Subject: [PATCH 522/607] Adopt ensureNoDisposablesAreLeakedInTestSuite for chatModel.test.ts --- .../contrib/chat/test/common/chatModel.test.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index ea7cd3617a3..8cb6844d4f2 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; @@ -14,23 +14,19 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; suite('ChatModel', () => { - const testDisposables = new DisposableStore(); + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); let instantiationService: TestInstantiationService; - suiteSetup(async () => { + setup(async () => { instantiationService = testDisposables.add(new TestInstantiationService()); - instantiationService.stub(IStorageService, new TestStorageService()); + instantiationService.stub(IStorageService, testDisposables.add(new TestStorageService())); instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IExtensionService, new TestExtensionService()); }); - teardown(() => { - testDisposables.clear(); - }); - test('Waits for initialization', async () => { - const model = new ChatModel('provider', undefined, new NullLogService()); + const model = testDisposables.add(new ChatModel('provider', undefined, new NullLogService())); let hasInitialized = false; model.waitForInitialization().then(() => { @@ -46,7 +42,7 @@ suite('ChatModel', () => { }); test('Initialization fails when model is disposed', async () => { - const model = new ChatModel('provider', undefined, new NullLogService()); + const model = testDisposables.add(new ChatModel('provider', undefined, new NullLogService())); model.dispose(); await assert.rejects(() => model.waitForInitialization()); From e1b3512105ec308fdab4aa316d85c64e072df20a Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 5 Sep 2023 15:54:32 -0700 Subject: [PATCH 523/607] Fix inline status row margins (#192212) --- src/vs/workbench/contrib/inlineChat/browser/inlineChat.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css index fe2e0ccde7a..87f586d2f02 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css @@ -99,14 +99,14 @@ /* status */ .monaco-editor .inline-chat .status { - margin-top: 2px; + margin-top: 4px; display: flex; justify-content: space-between; align-items: center; } .monaco-editor .inline-chat .status.actions { - margin-top: 2px; + margin-top: 6px; } .monaco-editor .inline-chat .status .actions.hidden { From 1b8f1ff9cdca1976109b10a44913607c880022b9 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 16:16:53 -0700 Subject: [PATCH 524/607] Adopt ensureNoDisposablesAreLeakedInTestSuite for chatService.test.ts --- .../contrib/chat/common/chatSlashCommands.ts | 14 +++--- .../chat/test/common/chatService.test.ts | 50 ++++++++----------- 2 files changed, 29 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index b64d5e6a5b8..5fbe93182bd 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -7,7 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Event, Emitter } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import { DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress } from 'vs/platform/progress/common/progress'; @@ -91,18 +91,18 @@ export interface IChatSlashCommandService { type Tuple = { data: IChatSlashData; command?: IChatSlashCallback }; -export class ChatSlashCommandService implements IChatSlashCommandService { +export class ChatSlashCommandService extends Disposable implements IChatSlashCommandService { declare _serviceBrand: undefined; private readonly _commands = new Map(); - private readonly _onDidChangeCommands = new Emitter(); + private readonly _onDidChangeCommands = this._register(new Emitter()); readonly onDidChangeCommands: Event = this._onDidChangeCommands.event; constructor(@IExtensionService private readonly _extensionService: IExtensionService) { - - const contributions = new DisposableStore(); + super(); + const contributions = this._register(new DisposableStore()); slashesExtPoint.setHandler(extensions => { contributions.clear(); @@ -128,9 +128,9 @@ export class ChatSlashCommandService implements IChatSlashCommandService { }); } - dispose(): void { + override dispose(): void { + super.dispose(); this._commands.clear(); - this._onDidChangeCommands.dispose(); } registerSlashData(data: IChatSlashData): IDisposable { diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index ca968ebf1c5..10fcf9f8bf4 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -4,26 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Emitter } from 'vs/base/common/event'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ProviderResult } from 'vs/editor/common/languages'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IViewsService } from 'vs/workbench/common/views'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; -import { IChatProgress, IChatProvider, IChatRequest, IChatResponse, IChat, ISlashCommand, IPersistedChatState } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChat, IChatProgress, IChatProvider, IChatRequest, IChatResponse, IPersistedChatState, ISlashCommand } from 'vs/workbench/contrib/chat/common/chatService'; import { ChatService } from 'vs/workbench/contrib/chat/common/chatServiceImpl'; +import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; +import { ChatVariablesService, IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { TestContextService, TestExtensionService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ChatSlashCommandService, IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; -import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { ChatVariablesService, IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; class SimpleTestProvider extends Disposable implements IChatProvider { private static sessionId = 0; @@ -59,35 +60,28 @@ class SimpleTestProvider extends Disposable implements IChatProvider { } suite('Chat', () => { - const testDisposables = new DisposableStore(); + const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); let storageService: IStorageService; let instantiationService: TestInstantiationService; - suiteSetup(async () => { - instantiationService = new TestInstantiationService(new ServiceCollection( + setup(async () => { + instantiationService = testDisposables.add(new TestInstantiationService(new ServiceCollection( [IChatSlashCommandService, new SyncDescriptor(ChatSlashCommandService)], [IChatVariablesService, new SyncDescriptor(ChatVariablesService)] - )); - instantiationService.stub(IStorageService, storageService = new TestStorageService()); + ))); + instantiationService.stub(IStorageService, storageService = testDisposables.add(new TestStorageService())); instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IExtensionService, new TestExtensionService()); + // instantiationService.stub(IChatSlashCommandService, testDisposables.add(instantiationService.createInstance(ChatSlashCommandService))); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IViewsService, new TestExtensionService()); instantiationService.stub(IChatContributionService, new TestExtensionService()); instantiationService.stub(IWorkspaceContextService, new TestContextService()); }); - suiteTeardown(() => { - instantiationService.dispose(); - }); - - teardown(() => { - testDisposables.clear(); - }); - test('retrieveSession', async () => { - const testService = instantiationService.createInstance(ChatService); + const testService = testDisposables.add(instantiationService.createInstance(ChatService)); const provider1 = new SimpleTestProvider('provider1'); const provider2 = new SimpleTestProvider('provider2'); testService.registerProvider(provider1); @@ -107,7 +101,7 @@ suite('Chat', () => { provider2.changeState({ state: 'provider2_state' }); storageService.flush(); - const testService2 = instantiationService.createInstance(ChatService); + const testService2 = testDisposables.add(instantiationService.createInstance(ChatService)); testService2.registerProvider(provider1); testService2.registerProvider(provider2); const retrieved1 = testService2.getOrRestoreSession(session1.sessionId); @@ -136,7 +130,7 @@ suite('Chat', () => { }; } - const testService = instantiationService.createInstance(ChatService); + const testService = testDisposables.add(instantiationService.createInstance(ChatService)); const provider1 = getFailProvider('provider1'); testService.registerProvider(provider1); @@ -145,7 +139,7 @@ suite('Chat', () => { }); test('Can\'t register same provider id twice', async () => { - const testService = instantiationService.createInstance(ChatService); + const testService = testDisposables.add(instantiationService.createInstance(ChatService)); const id = 'testProvider'; testService.registerProvider({ id, @@ -173,7 +167,7 @@ suite('Chat', () => { }); test('getSlashCommands', async () => { - const testService = instantiationService.createInstance(ChatService); + const testService = testDisposables.add(instantiationService.createInstance(ChatService)); const provider = new class extends SimpleTestProvider { constructor() { super('testProvider'); @@ -202,7 +196,7 @@ suite('Chat', () => { }); test('sendRequestToProvider', async () => { - const testService = instantiationService.createInstance(ChatService); + const testService = testDisposables.add(instantiationService.createInstance(ChatService)); testService.registerProvider(new SimpleTestProvider('testProvider')); const model = testService.startSession('testProvider', CancellationToken.None); @@ -213,7 +207,7 @@ suite('Chat', () => { }); test('addCompleteRequest', async () => { - const testService = instantiationService.createInstance(ChatService); + const testService = testDisposables.add(instantiationService.createInstance(ChatService)); testService.registerProvider(new SimpleTestProvider('testProvider')); const model = testService.startSession('testProvider', CancellationToken.None); From 033ed7a9f358b41fc62ad11c0173d1cb4d47f2aa Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 16:20:42 -0700 Subject: [PATCH 525/607] Cleanup --- src/vs/workbench/contrib/chat/test/common/chatService.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index 10fcf9f8bf4..c340299f47a 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -73,7 +73,6 @@ suite('Chat', () => { instantiationService.stub(IStorageService, storageService = testDisposables.add(new TestStorageService())); instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IExtensionService, new TestExtensionService()); - // instantiationService.stub(IChatSlashCommandService, testDisposables.add(instantiationService.createInstance(ChatSlashCommandService))); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IViewsService, new TestExtensionService()); instantiationService.stub(IChatContributionService, new TestExtensionService()); From 4de3a2cb9d86dbd66fac98a0886d84eef0a28b9d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 5 Sep 2023 16:27:21 -0700 Subject: [PATCH 526/607] debt: adopt ensureNoDisposablesAreLeakedInTestSuite (#192226) Finishes adoption in test I own --- src/vs/workbench/api/common/extHostTesting.ts | 9 ++- .../api/test/browser/extHostTesting.test.ts | 35 +++++---- .../browser/mainThreadManagedSockets.test.ts | 11 +-- .../test/browser/mainThreadTreeViews.test.ts | 23 +++--- .../notebookRendererMessagingService.test.ts | 7 +- .../test/browser/terminalTypeAhead.test.ts | 71 +++++++++++-------- .../testing/common/testResultService.ts | 17 +++-- .../hierarchalByName.test.ts | 11 +-- .../test/common/testResultService.test.ts | 23 +++--- .../contrib/testing/test/common/testStubs.ts | 1 + .../test/browser/workbenchTestServices.ts | 2 +- 11 files changed, 125 insertions(+), 85 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index b8096372fae..3c29e080e8c 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -606,6 +606,11 @@ class TestRunTracker extends Disposable { this.proxy.$addTestsToRun(this.dto.controllerId, this.dto.id, chain); } + + public override dispose(): void { + this.markEnded(); + super.dispose(); + } } /** @@ -676,7 +681,7 @@ export class TestRunCoordinator { }); const tracker = this.getTracker(request, dto, extension); - tracker.onEnd(() => { + Event.once(tracker.onEnd)(() => { this.proxy.$finishedExtensionTestRun(dto.id); tracker.dispose(); }); @@ -687,7 +692,7 @@ export class TestRunCoordinator { private getTracker(req: vscode.TestRunRequest, dto: TestRunDto, extension: IRelaxedExtensionDescription, token?: CancellationToken) { const tracker = new TestRunTracker(dto, this.proxy, extension, token); this.tracked.set(req, tracker); - tracker.onEnd(() => this.tracked.delete(req)); + Event.once(tracker.onEnd)(() => this.tracked.delete(req)); return tracker; } } diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index 085bf16d5ab..0a7b6004539 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -10,6 +10,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Iterable } from 'vs/base/common/iterator'; import { URI } from 'vs/base/common/uri'; import { mockObject, MockObject } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import * as editorRange from 'vs/editor/common/core/range'; import { IRelaxedExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MainThreadTestingShape } from 'vs/workbench/api/common/extHost.protocol'; @@ -71,11 +72,17 @@ suite('ExtHost Testing', () => { } } + teardown(() => { + sinon.restore(); + }); + + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + let single: TestExtHostTestItemCollection; setup(() => { - single = new TestExtHostTestItemCollection('ctrlId', 'root', { + single = ds.add(new TestExtHostTestItemCollection('ctrlId', 'root', { getDocument: () => undefined, - } as Partial as ExtHostDocumentsAndEditors); + } as Partial as ExtHostDocumentsAndEditors)); single.resolveHandler = item => { if (item === undefined) { const a = new TestItemImpl('ctrlId', 'id-a', 'a', URI.file('/')); @@ -89,12 +96,7 @@ suite('ExtHost Testing', () => { } }; - single.onDidGenerateDiff(d => single.setDiff(d /* don't clear during testing */)); - }); - - teardown(() => { - single.dispose(); - sinon.restore(); + ds.add(single.onDidGenerateDiff(d => single.setDiff(d /* don't clear during testing */))); }); suite('OwnedTestCollection', () => { @@ -623,7 +625,7 @@ suite('ExtHost Testing', () => { }); test('tracks a run started from a main thread request', () => { - const tracker = c.prepareForMainThreadTestRun(req, dto, ext, cts.token); + const tracker = ds.add(c.prepareForMainThreadTestRun(req, dto, ext, cts.token)); assert.strictEqual(tracker.hasRunningTasks, false); const task1 = c.createTestRun(ext, 'ctrl', single, req, 'run1', true); @@ -648,10 +650,10 @@ suite('ExtHost Testing', () => { test('run cancel force ends after a timeout', () => { const clock = sinon.useFakeTimers(); try { - const tracker = c.prepareForMainThreadTestRun(req, dto, ext, cts.token); + const tracker = ds.add(c.prepareForMainThreadTestRun(req, dto, ext, cts.token)); const task = c.createTestRun(ext, 'ctrl', single, req, 'run1', true); const onEnded = sinon.stub(); - tracker.onEnd(onEnded); + ds.add(tracker.onEnd(onEnded)); assert.strictEqual(task.token.isCancellationRequested, false); assert.strictEqual(tracker.hasRunningTasks, true); @@ -673,10 +675,10 @@ suite('ExtHost Testing', () => { }); test('run cancel force ends on second cancellation request', () => { - const tracker = c.prepareForMainThreadTestRun(req, dto, ext, cts.token); + const tracker = ds.add(c.prepareForMainThreadTestRun(req, dto, ext, cts.token)); const task = c.createTestRun(ext, 'ctrl', single, req, 'run1', true); const onEnded = sinon.stub(); - tracker.onEnd(onEnded); + ds.add(tracker.onEnd(onEnded)); assert.strictEqual(task.token.isCancellationRequested, false); assert.strictEqual(tracker.hasRunningTasks, true); @@ -753,6 +755,8 @@ suite('ExtHost Testing', () => { task1.passed(single.root.children.get('id-a')!.children.get('id-ab')!); assert.deepStrictEqual(proxy.$addTestsToRun.args, expectedArgs); + + task1.end(); }); test('adds test messages to run', () => { @@ -796,6 +800,8 @@ suite('ExtHost Testing', () => { location: convert.location.from({ uri: test2.uri!, range: test2.range! }), }] ]); + + task.end(); }); test('guards calls after runs are ended', () => { @@ -829,6 +835,7 @@ suite('ExtHost Testing', () => { TestResultState.Passed, undefined, ]]); + task.end(); }); test('sets state of test with identical local IDs (#131827)', () => { @@ -856,6 +863,8 @@ suite('ExtHost Testing', () => { [single.root, testB, childB].map(t => convert.TestItem.from(t as TestItemImpl)), ], ]); + + task1.end(); }); }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts b/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts index 6eb1c08ad65..8a714d8130d 100644 --- a/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadManagedSockets.test.ts @@ -10,12 +10,15 @@ import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { SocketCloseEvent } from 'vs/base/parts/ipc/common/ipc.net'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { RemoteSocketHalf } from 'vs/platform/remote/common/managedSocket'; import { MainThreadManagedSocket } from 'vs/workbench/api/browser/mainThreadManagedSockets'; import { ExtHostManagedSocketsShape } from 'vs/workbench/api/common/extHost.protocol'; suite('MainThreadManagedSockets', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + suite('ManagedSocket', () => { let extHost: ExtHostMock; let half: RemoteSocketHalf; @@ -72,7 +75,7 @@ suite('MainThreadManagedSockets', () => { const socket = MainThreadManagedSocket.connect(1, extHost, '/hello', 'world=true', '', half); await extHost.expectEvent(evt => evt.data && evt.data.startsWith('GET ws://localhost/hello?world=true&skipWebSocketFrames=true HTTP/1.1\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Key:'), 'websocket open event'); half.onData.fire(VSBuffer.fromString('Opened successfully ;)\r\n\r\n')); - return await socket; + return ds.add(await socket); } test('connects', async () => { @@ -83,10 +86,10 @@ suite('MainThreadManagedSockets', () => { const socketProm = MainThreadManagedSocket.connect(1, extHost, '/hello', 'world=true', '', half); await extHost.expectEvent(evt => evt.data && evt.data.includes('GET ws://localhost'), 'websocket open event'); half.onData.fire(VSBuffer.fromString('Opened successfully ;)\r\n\r\nSome trailing data')); - const socket = await socketProm; + const socket = ds.add(await socketProm); const data: string[] = []; - socket.onData(d => data.push(d.toString())); + ds.add(socket.onData(d => data.push(d.toString()))); await timeout(1); // allow microtasks to flush assert.deepStrictEqual(data, ['Some trailing data']); }); @@ -94,7 +97,7 @@ suite('MainThreadManagedSockets', () => { test('round trips data', async () => { const socket = await doConnect(); const data: string[] = []; - socket.onData(d => data.push(d.toString())); + ds.add(socket.onData(d => data.push(d.toString()))); socket.write(VSBuffer.fromString('ping')); await extHost.expectEvent(evt => evt.data === 'ping', 'expected ping'); diff --git a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts index f796cae8ed2..249e6316dba 100644 --- a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { DisposableStore } from 'vs/base/common/lifecycle'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -45,25 +45,29 @@ suite('MainThreadHostTreeView', function () { let container: ViewContainer; let mainThreadTreeViews: MainThreadTreeViews; let extHostTreeViewsShape: MockExtHostTreeViewsShape; - let disposables: DisposableStore; + + teardown(() => { + ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container), container); + }); + + const disposables = ensureNoDisposablesAreLeakedInTestSuite(); setup(async () => { - disposables = new DisposableStore(); const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposables); - const viewDescriptorService = instantiationService.createInstance(ViewDescriptorService); + const viewDescriptorService = disposables.add(instantiationService.createInstance(ViewDescriptorService)); instantiationService.stub(IViewDescriptorService, viewDescriptorService); container = Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ id: 'testContainer', title: { value: 'test', original: 'test' }, ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); const viewDescriptor: ITreeViewDescriptor = { id: testTreeViewId, ctorDescriptor: null!, name: 'Test View 1', - treeView: instantiationService.createInstance(CustomTreeView, 'testTree', 'Test Title', 'extension.id'), + treeView: disposables.add(instantiationService.createInstance(CustomTreeView, 'testTree', 'Test Title', 'extension.id')), }; ViewsRegistry.registerViews([viewDescriptor], container); const testExtensionService = new TestExtensionService(); extHostTreeViewsShape = new MockExtHostTreeViewsShape(); - mainThreadTreeViews = new MainThreadTreeViews( + mainThreadTreeViews = disposables.add(new MainThreadTreeViews( new class implements IExtHostContext { remoteAuthority = ''; extensionHostKind = ExtensionHostKind.LocalProcess; @@ -74,16 +78,11 @@ suite('MainThreadHostTreeView', function () { return extHostTreeViewsShape; } drain(): any { return null; } - }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService()); + }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService())); mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false, manuallyManageCheckboxes: false }); await testExtensionService.whenInstalledExtensionsRegistered(); }); - teardown(() => { - ViewsRegistry.deregisterViews(ViewsRegistry.getViews(container), container); - disposables.dispose(); - }); - test('getChildren keeps custom properties', async () => { const treeView: ITreeView = (ViewsRegistry.getView(testTreeViewId)).treeView; const children = await treeView.dataProvider?.getChildren({ handle: 'root', collapsibleState: TreeItemCollapsibleState.Expanded }); diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookRendererMessagingService.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookRendererMessagingService.test.ts index 3aee4e1668b..db9868edfa7 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookRendererMessagingService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookRendererMessagingService.test.ts @@ -8,17 +8,20 @@ import { stub } from 'sinon'; import { NotebookRendererMessagingService } from 'vs/workbench/contrib/notebook/browser/services/notebookRendererMessagingServiceImpl'; import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('NotebookRendererMessaging', () => { let extService: NullExtensionService; let m: NotebookRendererMessagingService; let sent: unknown[] = []; + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + setup(() => { sent = []; extService = new NullExtensionService(); - m = new NotebookRendererMessagingService(extService); - m.onShouldPostMessage(e => sent.push(e)); + m = ds.add(new NotebookRendererMessagingService(extService)); + ds.add(m.onShouldPostMessage(e => sent.push(e))); }); test('activates on prepare', () => { diff --git a/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts b/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts index 12fdba7c7d3..bbf3e024304 100644 --- a/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts @@ -11,6 +11,8 @@ import { CharPredictState, IPrediction, PredictionStats, TypeAheadAddon } from ' import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; const CSI = `\x1b[`; @@ -20,18 +22,24 @@ const enum CursorMoveDirection { } suite('Workbench - Terminal Typeahead', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + suite('PredictionStats', () => { let stats: PredictionStats; - const add = new Emitter(); - const succeed = new Emitter(); - const fail = new Emitter(); + let add: Emitter; + let succeed: Emitter; + let fail: Emitter; setup(() => { - stats = new PredictionStats({ + add = ds.add(new Emitter()); + succeed = ds.add(new Emitter()); + fail = ds.add(new Emitter()); + + stats = ds.add(new PredictionStats({ onPredictionAdded: add.event, onPredictionSucceeded: succeed.event, onPredictionFailed: fail.event, - } as any); + } as any)); }); test('creates sane data', () => { @@ -74,7 +82,7 @@ suite('Workbench - Terminal Typeahead', () => { }); suite('timeline', () => { - const onBeforeProcessData = new Emitter(); + let onBeforeProcessData: Emitter; let publicLog: SinonStub; let config: ITerminalConfiguration; let addon: TestTypeAheadAddon; @@ -94,6 +102,7 @@ suite('Workbench - Terminal Typeahead', () => { }; setup(() => { + onBeforeProcessData = ds.add(new Emitter()); config = upcastPartial({ localEchoStyle: 'italic', localEchoLatencyThreshold: 0, @@ -113,14 +122,14 @@ suite('Workbench - Terminal Typeahead', () => { }); test('predicts a single character', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('o'); t.expectWritten(`${CSI}3mo${CSI}23m`); }); test('validates character prediction', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('o'); expectProcessed('o', predictedHelloo); @@ -128,7 +137,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('validates zsh prediction (#112842)', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('o'); expectProcessed('o', predictedHelloo); @@ -145,7 +154,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('does not validate zsh prediction on differing lookbehindn (#112842)', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('o'); expectProcessed('o', predictedHelloo); @@ -163,7 +172,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('rolls back character prediction', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('o'); @@ -179,7 +188,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('handles left arrow when we hit the boundary', () => { - const t = createMockTerminal({ lines: ['|'] }); + const t = ds.add(createMockTerminal({ lines: ['|'] })); addon.activate(t.terminal); addon.unlockNavigating(); @@ -198,7 +207,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('handles right arrow when we hit the boundary', () => { - const t = createMockTerminal({ lines: ['|'] }); + const t = ds.add(createMockTerminal({ lines: ['|'] })); addon.activate(t.terminal); addon.unlockNavigating(); @@ -217,7 +226,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('internal cursor state is reset when all predictions are undone', () => { - const t = createMockTerminal({ lines: ['|'] }); + const t = ds.add(createMockTerminal({ lines: ['|'] })); addon.activate(t.terminal); addon.unlockNavigating(); @@ -234,10 +243,10 @@ suite('Workbench - Terminal Typeahead', () => { }); test('restores cursor graphics mode', () => { - const t = createMockTerminal({ + const t = ds.add(createMockTerminal({ lines: ['hello|'], cursorAttrs: { isAttributeDefault: false, isBold: true, isFgPalette: true, getFgColor: 1 }, - }); + })); addon.activate(t.terminal); t.onData('o'); @@ -253,7 +262,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('validates against and applies graphics mode on predicted', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('o'); expectProcessed(`${CSI}4mo`, [ @@ -268,7 +277,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('ignores cursor hides or shows', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('o'); expectProcessed(`${CSI}?25lo${CSI}?25h`, [ @@ -284,7 +293,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('matches backspace at EOL (bash style)', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('\x7F'); expectProcessed(`\b${CSI}K`, `\b${CSI}K`); @@ -292,7 +301,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('matches backspace at EOL (zsh style)', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('\x7F'); expectProcessed('\b \b', '\b \b'); @@ -300,7 +309,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('gradually matches backspace', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); t.onData('\x7F'); expectProcessed('\b', ''); @@ -309,7 +318,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('restores old character after invalid backspace', () => { - const t = createMockTerminal({ lines: ['hel|lo'] }); + const t = ds.add(createMockTerminal({ lines: ['hel|lo'] })); addon.activate(t.terminal); addon.unlockNavigating(); t.onData('\x7F'); @@ -319,7 +328,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('waits for validation before deleting to left of cursor', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); // initially should not backspace (until the server confirms it) @@ -340,7 +349,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('waits for first valid prediction on a line', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.lockMakingPredictions(); addon.activate(t.terminal); @@ -353,7 +362,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('disables on title change', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.activate(t.terminal); addon.reevaluateNow(); @@ -369,7 +378,7 @@ suite('Workbench - Terminal Typeahead', () => { }); test('adds line wrap prediction even if behind a boundary', () => { - const t = createMockTerminal({ lines: ['hello|'] }); + const t = ds.add(createMockTerminal({ lines: ['hello|'] })); addon.lockMakingPredictions(); addon.activate(t.terminal); @@ -441,11 +450,12 @@ function createMockTerminal({ lines, cursorAttrs }: { lines: string[]; cursorAttrs?: any; }) { + const ds = new DisposableStore(); const written: string[] = []; const cursor = { y: 1, x: 1 }; - const onTitleChange = new Emitter(); - const onData = new Emitter(); - const csiEmitter = new Emitter(); + const onTitleChange = ds.add(new Emitter()); + const onData = ds.add(new Emitter()); + const csiEmitter = ds.add(new Emitter()); for (let y = 0; y < lines.length; y++) { const line = lines[y]; @@ -468,6 +478,7 @@ function createMockTerminal({ lines, cursorAttrs }: { onData: (s: string) => onData.fire(s), csiEmitter, onTitleChange, + dispose: () => ds.dispose(), terminal: { cols: 80, rows: 5, @@ -476,7 +487,7 @@ function createMockTerminal({ lines, cursorAttrs }: { onTitleChange: onTitleChange.event, parser: { registerCsiHandler(_: unknown, callback: () => void) { - csiEmitter.event(callback); + ds.add(csiEmitter.event(callback)); }, }, write(line: string) { diff --git a/src/vs/workbench/contrib/testing/common/testResultService.ts b/src/vs/workbench/contrib/testing/common/testResultService.ts index e931cef40ac..3a4563396db 100644 --- a/src/vs/workbench/contrib/testing/common/testResultService.ts +++ b/src/vs/workbench/contrib/testing/common/testResultService.ts @@ -7,7 +7,7 @@ import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { once } from 'vs/base/common/functional'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -71,11 +71,12 @@ const isRunningTests = (service: ITestResultService) => export const ITestResultService = createDecorator('testResultService'); -export class TestResultService implements ITestResultService { +export class TestResultService extends Disposable implements ITestResultService { declare _serviceBrand: undefined; - private changeResultEmitter = new Emitter(); + private changeResultEmitter = this._register(new Emitter()); private _results: ITestResult[] = []; - private testChangeEmitter = new Emitter(); + private readonly _resultsDisposables: DisposableStore[] = []; + private testChangeEmitter = this._register(new Emitter()); /** * @inheritdoc @@ -110,6 +111,8 @@ export class TestResultService implements ITestResultService { @ITestResultStorage private readonly storage: ITestResultStorage, @ITestProfileService private readonly testProfiles: ITestProfileService, ) { + super(); + this._register(toDisposable(() => dispose(this._resultsDisposables))); this.isRunning = TestingContextKeys.isRunning.bindTo(contextKeyService); this.hasAnyResults = TestingContextKeys.hasAnyResults.bindTo(contextKeyService); } @@ -177,10 +180,14 @@ export class TestResultService implements ITestResultService { this.hasAnyResults.set(true); if (this.results.length > RETAIN_MAX_RESULTS) { this.results.pop(); + this._resultsDisposables.pop()?.dispose(); } + const ds = new DisposableStore(); + this._resultsDisposables.push(ds); + if (result instanceof LiveTestResult) { - const ds = new DisposableStore(); + ds.add(result); ds.add(result.onComplete(() => this.onComplete(result))); ds.add(result.onChange(this.testChangeEmitter.fire, this.testChangeEmitter)); this.isRunning.set(true); diff --git a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts index 498971646ed..ea3ab5dfe0f 100644 --- a/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/explorerProjections/hierarchalByName.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ListProjection } from 'vs/workbench/contrib/testing/browser/explorerProjections/listProjection'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { TestResultItemChange } from 'vs/workbench/contrib/testing/common/testResult'; @@ -17,6 +18,12 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { let onTestChanged: Emitter; let resultsService: any; + teardown(() => { + harness.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + setup(() => { onTestChanged = new Emitter(); resultsService = { @@ -28,10 +35,6 @@ suite('Workbench - Testing Explorer Hierarchal by Name Projection', () => { harness = new TestTreeTestHarness(l => new ListProjection({}, l, resultsService as any)); }); - teardown(() => { - harness.dispose(); - }); - test('renders initial tree', () => { harness.flush(); assert.deepStrictEqual(harness.tree.getRendered(), [ diff --git a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts index 464cceb9478..b29f1b7f71f 100644 --- a/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts +++ b/src/vs/workbench/contrib/testing/test/common/testResultService.test.ts @@ -6,7 +6,8 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { NullLogService } from 'vs/platform/log/common/log'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; @@ -27,7 +28,6 @@ suite('Workbench - Test Results Service', () => { let r: TestLiveTestResult; let changed = new Set(); let tests: TestTestCollection; - let ds: DisposableStore; const defaultOpts = (testIds: string[]): ResolvedTestRunRequest => ({ targets: [{ @@ -53,8 +53,9 @@ suite('Workbench - Test Results Service', () => { } } + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + setup(async () => { - ds = new DisposableStore(); changed = new Set(); r = ds.add(new TestLiveTestResult( 'foo', @@ -62,14 +63,16 @@ suite('Workbench - Test Results Service', () => { defaultOpts(['id-a']), )); - r.onChange(e => changed.add(e)); + ds.add(r.onChange(e => changed.add(e))); r.addTask({ id: 't', name: undefined, running: true }); tests = ds.add(testStubs.nested()); + const cts = ds.add(new CancellationTokenSource()); const ok = await Promise.race([ Promise.resolve(tests.expand(tests.root.id, Infinity)).then(() => true), - timeout(1000).then(() => false), + timeout(1000, cts.token).then(() => false), ]); + cts.cancel(); // todo@connor4312: debug for tests #137853: if (!ok) { @@ -88,10 +91,6 @@ suite('Workbench - Test Results Service', () => { ]); }); - teardown(() => { - ds.dispose(); - }); - // ensureNoDisposablesAreLeakedInTestSuite(); todo@connor4312 suite('LiveTestResult', () => { @@ -209,7 +208,7 @@ suite('Workbench - Test Results Service', () => { setup(() => { storage = ds.add(new InMemoryResultStorage(ds.add(new TestStorageService()), new NullLogService())); - results = new TestTestResultService(new MockContextKeyService(), storage, ds.add(new TestProfileService(new MockContextKeyService(), ds.add(new TestStorageService())))); + results = ds.add(new TestTestResultService(new MockContextKeyService(), storage, ds.add(new TestProfileService(new MockContextKeyService(), ds.add(new TestStorageService()))))); }); test('pushes new result', () => { @@ -223,11 +222,11 @@ suite('Workbench - Test Results Service', () => { r.markComplete(); await timeout(10); // allow persistImmediately async to happen - results = new TestResultService( + results = ds.add(new TestResultService( new MockContextKeyService(), storage, ds.add(new TestProfileService(new MockContextKeyService(), ds.add(new TestStorageService()))), - ); + )); assert.strictEqual(0, results.results.length); await timeout(10); // allow load promise to resolve diff --git a/src/vs/workbench/contrib/testing/test/common/testStubs.ts b/src/vs/workbench/contrib/testing/test/common/testStubs.ts index a8770a29e15..e2c41e8a2b7 100644 --- a/src/vs/workbench/contrib/testing/test/common/testStubs.ts +++ b/src/vs/workbench/contrib/testing/test/common/testStubs.ts @@ -109,6 +109,7 @@ export const getInitializedMainTestCollection = async (singleUse = testStubs.nes const c = new MainThreadTestCollection(async (t, l) => singleUse.expand(t, l)); await singleUse.expand(singleUse.root.id, Infinity); c.apply(singleUse.collectDiff()); + singleUse.dispose(); return c; }; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index ed87b23c2ba..1340c38c2de 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -241,7 +241,7 @@ export function workbenchInstantiationService( contextKeyService?: (instantiationService: IInstantiationService) => IContextKeyService; textEditorService?: (instantiationService: IInstantiationService) => ITextEditorService; }, - disposables: DisposableStore = new DisposableStore() + disposables: Pick = new DisposableStore() ): TestInstantiationService { const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()]))); From 996b733b2ccf6969fb99f6599a7a0f0540b9807a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Sep 2023 16:41:34 -0700 Subject: [PATCH 527/607] Adopt ensureNoDisposablesAreLeakedInTestSuite (#192221) For #190503 --- .../test/browser/parameterHintsModel.test.ts | 54 ++++++++-------- src/vs/workbench/api/common/extHostWebview.ts | 22 +++++-- .../api/common/extHostWebviewPanels.ts | 10 ++- .../api/test/browser/extHostWebview.test.ts | 64 +++++++++++-------- .../common/externalUriOpenerService.test.ts | 18 ++++-- 5 files changed, 100 insertions(+), 68 deletions(-) diff --git a/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts index 0b52a5a7850..013fa190f13 100644 --- a/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts @@ -8,6 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Position } from 'vs/editor/common/core/position'; import { Handler } from 'vs/editor/common/editorCommon'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; @@ -41,8 +42,7 @@ const emptySigHelpResult: languages.SignatureHelpResult = { suite('ParameterHintsModel', () => { const disposables = new DisposableStore(); - - let registry = new LanguageFeatureRegistry(); + let registry: LanguageFeatureRegistry; setup(() => { disposables.clear(); @@ -53,19 +53,28 @@ suite('ParameterHintsModel', () => { disposables.clear(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createMockEditor(fileContents: string) { - const textModel = createTextModel(fileContents, undefined, undefined, mockFile); - const editor = createTestCodeEditor(textModel, { + const textModel = disposables.add(createTextModel(fileContents, undefined, undefined, mockFile)); + const editor = disposables.add(createTestCodeEditor(textModel, { serviceCollection: new ServiceCollection( [ITelemetryService, NullTelemetryService], - [IStorageService, new InMemoryStorageService()] + [IStorageService, disposables.add(new InMemoryStorageService())] ) - }); - disposables.add(textModel); - disposables.add(editor); + })); return editor; } + function getNextHint(model: ParameterHintsModel) { + return new Promise(resolve => { + const sub = disposables.add(model.onChangedHints(e => { + sub.dispose(); + return resolve(e ? { value: e, dispose: () => { } } : undefined); + })); + }); + } + test('Provider should get trigger character on type', async () => { let done: () => void; const donePromise = new Promise(resolve => { done = resolve; }); @@ -148,8 +157,7 @@ suite('ParameterHintsModel', () => { const triggerChar = '('; const editor = createMockEditor(''); - const hintModel = new ParameterHintsModel(editor, registry); - disposables.add(hintModel); + const hintModel = disposables.add(new ParameterHintsModel(editor, registry)); let invokeCount = 0; disposables.add(registry.register(mockFileSelector, new class implements languages.SignatureHelpProvider { @@ -281,7 +289,7 @@ suite('ParameterHintsModel', () => { test('Should cancel existing request when new request comes in', async () => { const editor = createMockEditor('abc def'); - const hintsModel = new ParameterHintsModel(editor, registry); + const hintsModel = disposables.add(new ParameterHintsModel(editor, registry)); let didRequestCancellationOf = -1; let invokeCount = 0; @@ -293,7 +301,7 @@ suite('ParameterHintsModel', () => { provideSignatureHelp(_model: ITextModel, _position: Position, token: CancellationToken): languages.SignatureHelpResult | Promise { try { const count = invokeCount++; - token.onCancellationRequested(() => { didRequestCancellationOf = count; }); + disposables.add(token.onCancellationRequested(() => { didRequestCancellationOf = count; })); // retrigger on first request if (count === 0) { @@ -330,7 +338,7 @@ suite('ParameterHintsModel', () => { assert.strictEqual(-1, didRequestCancellationOf); return new Promise((resolve, reject) => - hintsModel.onChangedHints(newParamterHints => { + disposables.add(hintsModel.onChangedHints(newParamterHints => { try { assert.strictEqual(0, didRequestCancellationOf); assert.strictEqual('1', newParamterHints!.signatures[0].label); @@ -338,7 +346,7 @@ suite('ParameterHintsModel', () => { } catch (e) { reject(e); } - })); + }))); }); }); @@ -401,8 +409,7 @@ suite('ParameterHintsModel', () => { const paramterLabel = 'parameter'; const editor = createMockEditor(''); - const model = new ParameterHintsModel(editor, registry, 5); - disposables.add(model); + const model = disposables.add(new ParameterHintsModel(editor, registry, 5)); disposables.add(registry.register(mockFileSelector, new class implements languages.SignatureHelpProvider { signatureHelpTriggerCharacters = [triggerChar]; @@ -477,8 +484,7 @@ suite('ParameterHintsModel', () => { test('Quick typing should use the first trigger character', async () => { const editor = createMockEditor(''); - const model = new ParameterHintsModel(editor, registry, 50); - disposables.add(model); + const model = disposables.add(new ParameterHintsModel(editor, registry, 50)); const triggerCharacter = 'a'; @@ -519,8 +525,7 @@ suite('ParameterHintsModel', () => { const donePromise = new Promise(resolve => { done = resolve; }); const editor = createMockEditor(''); - const model = new ParameterHintsModel(editor, registry, 50); - disposables.add(model); + const model = disposables.add(new ParameterHintsModel(editor, registry, 50)); const triggerCharacter = 'a'; const retriggerCharacter = 'b'; @@ -570,12 +575,3 @@ suite('ParameterHintsModel', () => { }); }); }); - -function getNextHint(model: ParameterHintsModel) { - return new Promise(resolve => { - const sub = model.onChangedHints(e => { - sub.dispose(); - return resolve(e ? { value: e, dispose: () => { } } : undefined); - }); - }); -} diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 9d65e82faa4..367d4efbac2 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -7,16 +7,17 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import * as objects from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; -import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { normalizeVersion, parseVersion } from 'vs/platform/extensions/common/extensionValidator'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { deserializeWebviewMessage, serializeWebviewMessage } from 'vs/workbench/api/common/extHostWebviewMessaging'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; -import { asWebviewUri, webviewGenericCspSource, WebviewRemoteInfo } from 'vs/workbench/contrib/webview/common/webview'; +import { WebviewRemoteInfo, asWebviewUri, webviewGenericCspSource } from 'vs/workbench/contrib/webview/common/webview'; import { SerializableObjectWithBuffers } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import type * as vscode from 'vscode'; import * as extHostProtocol from './extHost.protocol'; @@ -190,7 +191,7 @@ function shouldTryRewritingOldResourceUris(extension: IExtensionDescription): bo } } -export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { +export class ExtHostWebviews extends Disposable implements extHostProtocol.ExtHostWebviewsShape { private readonly _webviewProxy: extHostProtocol.MainThreadWebviewsShape; @@ -203,9 +204,19 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { private readonly _logService: ILogService, private readonly _deprecationService: IExtHostApiDeprecationService, ) { + super(); this._webviewProxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviews); } + public override dispose(): void { + super.dispose(); + + for (const webview of this._webviews.values()) { + webview.dispose(); + } + this._webviews.clear(); + } + public $onMessage( handle: extHostProtocol.WebviewHandle, jsonMessage: string, @@ -229,7 +240,10 @@ export class ExtHostWebviews implements extHostProtocol.ExtHostWebviewsShape { const webview = new ExtHostWebview(handle, this._webviewProxy, reviveOptions(options), this.remoteInfo, this.workspace, extension, this._deprecationService); this._webviews.set(handle, webview); - webview._onDidDispose(() => { this._webviews.delete(handle); }); + const sub = webview._onDidDispose(() => { + sub.dispose(); + this.deleteWebview(handle); + }); return webview; } diff --git a/src/vs/workbench/api/common/extHostWebviewPanels.ts b/src/vs/workbench/api/common/extHostWebviewPanels.ts index c7f1717ebf5..6018c3718de 100644 --- a/src/vs/workbench/api/common/extHostWebviewPanels.ts +++ b/src/vs/workbench/api/common/extHostWebviewPanels.ts @@ -169,7 +169,7 @@ class ExtHostWebviewPanel extends Disposable implements vscode.WebviewPanel { } } -export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanelsShape { +export class ExtHostWebviewPanels extends Disposable implements extHostProtocol.ExtHostWebviewPanelsShape { private static newHandle(): extHostProtocol.WebviewHandle { return generateUuid(); @@ -189,9 +189,17 @@ export class ExtHostWebviewPanels implements extHostProtocol.ExtHostWebviewPanel private readonly webviews: ExtHostWebviews, private readonly workspace: IExtHostWorkspace | undefined, ) { + super(); this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadWebviewPanels); } + public override dispose(): void { + super.dispose(); + + this._webviewPanels.forEach(value => value.dispose()); + this._webviewPanels.clear(); + } + public createWebviewPanel( extension: IExtensionDescription, viewType: string, diff --git a/src/vs/workbench/api/test/browser/extHostWebview.test.ts b/src/vs/workbench/api/test/browser/extHostWebview.test.ts index 82d95eade2c..b741292bd00 100644 --- a/src/vs/workbench/api/test/browser/extHostWebview.test.ts +++ b/src/vs/workbench/api/test/browser/extHostWebview.test.ts @@ -4,43 +4,71 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { NullLogService } from 'vs/platform/log/common/log'; import { MainThreadWebviewManager } from 'vs/workbench/api/browser/mainThreadWebviewManager'; -import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { NullApiDeprecationService } from 'vs/workbench/api/common/extHostApiDeprecationService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostWebviews } from 'vs/workbench/api/common/extHostWebview'; import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels'; +import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { decodeAuthority, webviewResourceBaseHost } from 'vs/workbench/contrib/webview/common/webview'; import { EditorGroupColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; +import { IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import type * as vscode from 'vscode'; -import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; suite('ExtHostWebview', () => { - + let disposables: DisposableStore; let rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined; setup(() => { + disposables = new DisposableStore(); + const shape = createNoopMainThreadWebviews(); rpcProtocol = SingleProxyRPCProtocol(shape); }); + teardown(() => { + disposables.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + + function createWebview(rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined, remoteAuthority: string | undefined) { + const extHostWebviews = disposables.add(new ExtHostWebviews(rpcProtocol!, { + authority: remoteAuthority, + isRemote: !!remoteAuthority, + }, undefined, new NullLogService(), NullApiDeprecationService)); + + const extHostWebviewPanels = disposables.add(new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined)); + + return disposables.add(extHostWebviewPanels.createWebviewPanel({ + extensionLocation: URI.from({ + scheme: remoteAuthority ? Schemas.vscodeRemote : Schemas.file, + authority: remoteAuthority, + path: '/ext/path', + }) + } as IExtensionDescription, 'type', 'title', 1, {})); + } + test('Cannot register multiple serializers for the same view type', async () => { const viewType = 'view.type'; - const extHostWebviews = new ExtHostWebviews(rpcProtocol!, { authority: undefined, isRemote: false }, undefined, new NullLogService(), NullApiDeprecationService); + const extHostWebviews = disposables.add(new ExtHostWebviews(rpcProtocol!, { authority: undefined, isRemote: false }, undefined, new NullLogService(), NullApiDeprecationService)); - const extHostWebviewPanels = new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined); + const extHostWebviewPanels = disposables.add(new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined)); let lastInvokedDeserializer: vscode.WebviewPanelSerializer | undefined = undefined; class NoopSerializer implements vscode.WebviewPanelSerializer { - async deserializeWebviewPanel(_webview: vscode.WebviewPanel, _state: any): Promise { + async deserializeWebviewPanel(webview: vscode.WebviewPanel, _state: any): Promise { lastInvokedDeserializer = this; + disposables.add(webview); } } @@ -61,12 +89,12 @@ suite('ExtHostWebview', () => { assert.strictEqual(lastInvokedDeserializer, serializerA); assert.throws( - () => extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB), + () => disposables.add(extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB)), 'Should throw when registering two serializers for the same view'); serializerARegistration.dispose(); - extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB); + disposables.add(extHostWebviewPanels.registerWebviewPanelSerializer(extension, viewType, serializerB)); await extHostWebviewPanels.$deserializeWebviewPanel('x', viewType, { title: 'title', @@ -169,30 +197,12 @@ suite('ExtHostWebview', () => { }); }); -function createWebview(rpcProtocol: (IExtHostRpcService & IExtHostContext) | undefined, remoteAuthority: string | undefined) { - const extHostWebviews = new ExtHostWebviews(rpcProtocol!, { - authority: remoteAuthority, - isRemote: !!remoteAuthority, - }, undefined, new NullLogService(), NullApiDeprecationService); - - const extHostWebviewPanels = new ExtHostWebviewPanels(rpcProtocol!, extHostWebviews, undefined); - - const webview = extHostWebviewPanels.createWebviewPanel({ - extensionLocation: URI.from({ - scheme: remoteAuthority ? Schemas.vscodeRemote : Schemas.file, - authority: remoteAuthority, - path: '/ext/path', - }) - } as IExtensionDescription, 'type', 'title', 1, {}); - return webview; -} - function createNoopMainThreadWebviews() { return new class extends mock() { + $disposeWebview() { /* noop */ } $createWebviewPanel() { /* noop */ } $registerSerializer() { /* noop */ } $unregisterSerializer() { /* noop */ } }; } - diff --git a/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts b/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts index ec8685a9bdf..c97eb5396fd 100644 --- a/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts +++ b/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts @@ -5,8 +5,9 @@ import * as assert from 'assert'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ExternalUriOpenerPriority } from 'vs/editor/common/languages'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -36,11 +37,12 @@ class MockQuickInputService implements Partial{ } suite('ExternalUriOpenerService', () => { - + let disposables: DisposableStore; let instantiationService: TestInstantiationService; setup(() => { - instantiationService = new TestInstantiationService(); + disposables = new DisposableStore(); + instantiationService = disposables.add(new TestInstantiationService()); instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(IOpenerService, { @@ -49,11 +51,13 @@ suite('ExternalUriOpenerService', () => { }); teardown(() => { - instantiationService.dispose(); + disposables.dispose(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('Should not open if there are no openers', async () => { - const externalUriOpenerService: ExternalUriOpenerService = instantiationService.createInstance(ExternalUriOpenerService); + const externalUriOpenerService = disposables.add(instantiationService.createInstance(ExternalUriOpenerService)); externalUriOpenerService.registerExternalOpenerProvider(new class implements IExternalOpenerProvider { async *getOpeners(_targetUri: URI): AsyncGenerator { @@ -69,7 +73,7 @@ suite('ExternalUriOpenerService', () => { test('Should prompt if there is at least one enabled opener', async () => { instantiationService.stub(IQuickInputService, new MockQuickInputService(0)); - const externalUriOpenerService: ExternalUriOpenerService = instantiationService.createInstance(ExternalUriOpenerService); + const externalUriOpenerService = disposables.add(instantiationService.createInstance(ExternalUriOpenerService)); let openedWithEnabled = false; externalUriOpenerService.registerExternalOpenerProvider(new class implements IExternalOpenerProvider { @@ -99,7 +103,7 @@ suite('ExternalUriOpenerService', () => { }); test('Should automatically pick single preferred opener without prompt', async () => { - const externalUriOpenerService: ExternalUriOpenerService = instantiationService.createInstance(ExternalUriOpenerService); + const externalUriOpenerService = disposables.add(instantiationService.createInstance(ExternalUriOpenerService)); let openedWithPreferred = false; externalUriOpenerService.registerExternalOpenerProvider(new class implements IExternalOpenerProvider { From 630bbb19545b00c04b9ed2118ce817ec41b97898 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Sep 2023 16:41:49 -0700 Subject: [PATCH 528/607] Pick up latest markdown language service (#192242) --- extensions/markdown-language-features/server/package.json | 4 ++-- extensions/markdown-language-features/server/yarn.lock | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/extensions/markdown-language-features/server/package.json b/extensions/markdown-language-features/server/package.json index 1b3e549b748..f2a4cbb26c6 100644 --- a/extensions/markdown-language-features/server/package.json +++ b/extensions/markdown-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-markdown-languageserver", "description": "Markdown language server", - "version": "0.4.0-alpha.5", + "version": "0.4.0-alpha.6", "author": "Microsoft Corporation", "license": "MIT", "engines": { @@ -18,7 +18,7 @@ "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3", - "vscode-markdown-languageservice": "^0.4.0-alpha.5", + "vscode-markdown-languageservice": "^0.4.0-alpha.6", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock index c2a76d7f391..031015a55e2 100644 --- a/extensions/markdown-language-features/server/yarn.lock +++ b/extensions/markdown-language-features/server/yarn.lock @@ -128,10 +128,10 @@ vscode-languageserver@^8.1.0: dependencies: vscode-languageserver-protocol "3.17.3" -vscode-markdown-languageservice@^0.4.0-alpha.5: - version "0.4.0-alpha.5" - resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0-alpha.5.tgz#5a50dab354d07a4fb93b16cea43968f7a578c2b9" - integrity sha512-Pf183mpyAQ5WlsqXKlzSzPUACRlXxcEFGpn+vYsAx5m7r26hqtBTYHpTKzidIPqh6Xy797VGbtrOkvuwBmcCTg== +vscode-markdown-languageservice@^0.4.0-alpha.6: + version "0.4.0-alpha.6" + resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0-alpha.6.tgz#ec662d8185af99206f743d58eafb2e347131b9ea" + integrity sha512-M2P7/llYlOMHhL9rPMOe6u/hM7RJQkYk5SlNxXT8AzSoUe+MAdG6oVRL2gVW9hfg7+b9spBvXa938D9wnrA5Tg== dependencies: "@vscode/l10n" "^0.0.10" node-html-parser "^6.1.5" From 7cc7a6f50c2c23deddbbaddbaa22660c20c2001a Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 5 Sep 2023 16:56:58 -0700 Subject: [PATCH 529/607] Update md grammar (#192245) --- extensions/markdown-basics/cgmanifest.json | 2 +- extensions/markdown-basics/syntaxes/markdown.tmLanguage.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index bb88d5a8bf8..8dcb43d123f 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -33,7 +33,7 @@ "git": { "name": "microsoft/vscode-markdown-tm-grammar", "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", - "commitHash": "c635942289ebf40954e69cf3637aac906465ade8" + "commitHash": "0a4b23580308fdcfb4ab7b526e3e13ba17d436fb" } }, "license": "MIT", diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index dcfa85d6e93..3f96e33b010 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/c635942289ebf40954e69cf3637aac906465ade8", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/0a4b23580308fdcfb4ab7b526e3e13ba17d436fb", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2989,7 +2989,7 @@ "name": "punctuation.definition.link.title.end.markdown" } }, - "match": "(? Date: Tue, 5 Sep 2023 17:13:03 -0700 Subject: [PATCH 530/607] Fix invalid php snippet (#192246) Fix #189561 --- extensions/php/snippets/php.code-snippets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/php/snippets/php.code-snippets b/extensions/php/snippets/php.code-snippets index 5cc31f19661..3be8a89eb29 100644 --- a/extensions/php/snippets/php.code-snippets +++ b/extensions/php/snippets/php.code-snippets @@ -21,7 +21,7 @@ }, "$a <=> $b": { "prefix": "spaceship", - "body": "(${1:$$a} <=> ${2:$$b} === ${3|0,1,-1|})", + "body": "(${1:\\$a} <=> ${2:\\$b} === ${3|0,1,-1|})", "description": "Spaceship equality check" }, "attribute": { From 221d7c695c561cd06789f48bebd0e25397225457 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 17:19:30 -0700 Subject: [PATCH 531/607] Simplify --- src/vs/workbench/services/search/common/textSearchManager.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index 9210f766f2c..cb4af3dd4e9 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -30,8 +30,7 @@ export class TextSearchManager { search(onProgress: (matches: IFileMatch[]) => void, token: CancellationToken): Promise { const folderQueries = this.query.folderQueries || []; - const tokenSource = new CancellationTokenSource(); - const listener = token.onCancellationRequested(() => tokenSource.cancel()); + const tokenSource = new CancellationTokenSource(token); return new Promise((resolve, reject) => { this.collector = new TextSearchResultsCollector(onProgress); @@ -65,7 +64,6 @@ export class TextSearchManager { return this.searchInFolder(fq, r => onResult(r, i), tokenSource.token); })).then(results => { tokenSource.dispose(); - listener.dispose(); this.collector!.flush(); const someFolderHitLImit = results.some(result => !!result && !!result.limitHit); @@ -82,7 +80,6 @@ export class TextSearchManager { }); }, (err: Error) => { tokenSource.dispose(); - listener.dispose(); const errMsg = toErrorMessage(err); reject(new Error(errMsg)); }); From 513466213950ff0a64bf40cb1cd2cddb7f6eead2 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 5 Sep 2023 17:47:30 -0700 Subject: [PATCH 532/607] Remove CredentialsService & keytar (#192224) * Remove CredentialsService & keytar ref https://github.com/microsoft/vscode/issues/115215 fixes https://github.com/microsoft/vscode/issues/143395 * compile * remove imports * rip the bandaid --- .github/workflows/ci.yml | 3 +- build/.moduleignore | 7 - build/darwin/create-universal-app.js | 4 +- build/darwin/create-universal-app.ts | 2 +- .../src/common/keychain.ts | 2 - .../extension-browser.webpack.config.js | 3 - .../extension.webpack.config.js | 5 +- .../languagedetection.test.ts | 1 - package.json | 2 - remote/package.json | 1 - remote/yarn.lock | 214 +------------- src/tsconfig.json | 1 - src/vs/code/electron-main/app.ts | 9 - src/vs/code/electron-main/auth.ts | 58 ++-- .../credentials/common/credentials.ts | 84 ------ .../common/credentialsMainService.ts | 264 ------------------ .../electron-main/credentialsMainService.ts | 49 ---- .../node/credentialsMainService.ts | 51 ---- .../node/nativeModules.integrationTest.ts | 24 +- src/vs/server/node/serverServices.ts | 9 +- .../api/browser/extensionHost.contribution.ts | 1 - .../workbench/api/browser/mainThreadKeytar.ts | 41 --- .../api/browser/mainThreadSecretState.ts | 225 +-------------- .../workbench/api/common/extHost.protocol.ts | 9 - .../api/common/extHostRequireInterceptor.ts | 93 ------ .../parts/activitybar/activitybarActions.ts | 4 +- src/vs/workbench/browser/web.api.ts | 7 - src/vs/workbench/browser/web.main.ts | 12 +- .../browser/editSessionsStorageService.ts | 6 +- .../browser/authenticationService.ts | 7 +- .../credentials/browser/credentialsService.ts | 82 ------ .../electron-sandbox/credentialsService.ts | 9 - .../test/browser/credentialsService.test.ts | 61 ---- .../userDataSync/browser/userDataSyncInit.ts | 4 +- .../browser/userDataSyncWorkbenchService.ts | 4 +- src/vs/workbench/workbench.desktop.main.ts | 1 - src/vs/workbench/workbench.web.main.ts | 1 - yarn.lock | 94 +------ 38 files changed, 44 insertions(+), 1410 deletions(-) delete mode 100644 src/vs/platform/credentials/common/credentials.ts delete mode 100644 src/vs/platform/credentials/common/credentialsMainService.ts delete mode 100644 src/vs/platform/credentials/electron-main/credentialsMainService.ts delete mode 100644 src/vs/platform/credentials/node/credentialsMainService.ts delete mode 100644 src/vs/workbench/api/browser/mainThreadKeytar.ts delete mode 100644 src/vs/workbench/services/credentials/browser/credentialsService.ts delete mode 100644 src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts delete mode 100644 src/vs/workbench/services/credentials/test/browser/credentialsService.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cd32a2b50b9..4d49c5b204f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -221,8 +221,7 @@ jobs: - name: Compile Integration Tests run: yarn --cwd test/integration/browser compile - # This is required for keytar unittests, otherwise we hit - # https://github.com/atom/node-keytar/issues/76 + # This is required for SecretStorage unittests - name: Create temporary keychain run: | security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain diff --git a/build/.moduleignore b/build/.moduleignore index e4a19bbb762..d92dedf90f9 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -79,13 +79,6 @@ kerberos/src/** kerberos/node_modules/** !kerberos/**/*.node -keytar/binding.gyp -keytar/build/** -keytar/src/** -keytar/script/** -keytar/node_modules/** -!keytar/**/*.node - node-pty/binding.gyp node-pty/build/** node-pty/src/** diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index a85f394ad75..a01c222c35e 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -45,7 +45,7 @@ async function main(buildDir) { }); fs.writeFileSync(productJsonPath, JSON.stringify(productJson, null, '\t')); // Verify if native module architecture is correct - const findOutput = await (0, cross_spawn_promise_1.spawn)('find', [outAppPath, '-name', 'keytar.node']); + const findOutput = await (0, cross_spawn_promise_1.spawn)('find', [outAppPath, '-name', 'kerberos.node']); const lipoOutput = await (0, cross_spawn_promise_1.spawn)('lipo', ['-archs', findOutput.replace(/\n$/, '')]); if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') { throw new Error(`Invalid arch, got : ${lipoOutput}`); @@ -57,4 +57,4 @@ if (require.main === module) { process.exit(1); }); } -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXVuaXZlcnNhbC1hcHAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGUtdW5pdmVyc2FsLWFwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLDZCQUE2QjtBQUM3Qix5QkFBeUI7QUFDekIsdUVBQTREO0FBQzVELHFFQUFvRDtBQUVwRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUVuRCxLQUFLLFVBQVUsSUFBSSxDQUFDLFFBQWlCO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFeEMsSUFBSSxDQUFDLFFBQVEsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztLQUMxQztJQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ3JGLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQzFDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDL0YsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUNuRyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxpQkFBaUIsSUFBSSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFFakcsTUFBTSxJQUFBLDJDQUFnQixFQUFDO1FBQ3RCLFVBQVU7UUFDVixZQUFZO1FBQ1osV0FBVztRQUNYLGFBQWE7UUFDYixXQUFXLEVBQUU7WUFDWixjQUFjO1lBQ2QsYUFBYTtZQUNiLGVBQWU7WUFDZixlQUFlO1lBQ2YsWUFBWTtZQUNaLGNBQWM7WUFDZCxRQUFRO1NBQ1I7UUFDRCxVQUFVO1FBQ1YsS0FBSyxFQUFFLElBQUk7S0FDWCxDQUFDLENBQUM7SUFFSCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUU7UUFDMUIsc0JBQXNCLEVBQUUsa0JBQWtCO0tBQzFDLENBQUMsQ0FBQztJQUNILEVBQUUsQ0FBQyxhQUFhLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRTNFLGtEQUFrRDtJQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUEsMkJBQUssRUFBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLGFBQWEsQ0FBQyxDQUFDLENBQUM7SUFDN0UsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFBLDJCQUFLLEVBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsRixJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLGNBQWMsRUFBRTtRQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixVQUFVLEVBQUUsQ0FBQyxDQUFDO0tBQ3JEO0FBQ0YsQ0FBQztBQUVELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNLEVBQUU7SUFDNUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDakMsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2pCLENBQUMsQ0FBQyxDQUFDO0NBQ0gifQ== \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLXVuaXZlcnNhbC1hcHAuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJjcmVhdGUtdW5pdmVyc2FsLWFwcC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUE7OztnR0FHZ0c7O0FBRWhHLDZCQUE2QjtBQUM3Qix5QkFBeUI7QUFDekIsdUVBQTREO0FBQzVELHFFQUFvRDtBQUVwRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztBQUVuRCxLQUFLLFVBQVUsSUFBSSxDQUFDLFFBQWlCO0lBQ3BDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUM7SUFFeEMsSUFBSSxDQUFDLFFBQVEsRUFBRTtRQUNkLE1BQU0sSUFBSSxLQUFLLENBQUMsd0JBQXdCLENBQUMsQ0FBQztLQUMxQztJQUVELE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxjQUFjLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBQ3JGLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxRQUFRLEdBQUcsTUFBTSxDQUFDO0lBQzFDLE1BQU0sVUFBVSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLG1CQUFtQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3JFLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLHFCQUFxQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQ3pFLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVUsRUFBRSxXQUFXLEVBQUUsS0FBSyxFQUFFLG1CQUFtQixDQUFDLENBQUM7SUFDL0YsTUFBTSxhQUFhLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsbUJBQW1CLENBQUMsQ0FBQztJQUNuRyxNQUFNLFVBQVUsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxpQkFBaUIsSUFBSSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDekUsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDLENBQUM7SUFFakcsTUFBTSxJQUFBLDJDQUFnQixFQUFDO1FBQ3RCLFVBQVU7UUFDVixZQUFZO1FBQ1osV0FBVztRQUNYLGFBQWE7UUFDYixXQUFXLEVBQUU7WUFDWixjQUFjO1lBQ2QsYUFBYTtZQUNiLGVBQWU7WUFDZixlQUFlO1lBQ2YsWUFBWTtZQUNaLGNBQWM7WUFDZCxRQUFRO1NBQ1I7UUFDRCxVQUFVO1FBQ1YsS0FBSyxFQUFFLElBQUk7S0FDWCxDQUFDLENBQUM7SUFFSCxNQUFNLFdBQVcsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQyxZQUFZLENBQUMsZUFBZSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFDekUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxXQUFXLEVBQUU7UUFDMUIsc0JBQXNCLEVBQUUsa0JBQWtCO0tBQzFDLENBQUMsQ0FBQztJQUNILEVBQUUsQ0FBQyxhQUFhLENBQUMsZUFBZSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsV0FBVyxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBRTNFLGtEQUFrRDtJQUNsRCxNQUFNLFVBQVUsR0FBRyxNQUFNLElBQUEsMkJBQUssRUFBQyxNQUFNLEVBQUUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxFQUFFLGVBQWUsQ0FBQyxDQUFDLENBQUM7SUFDL0UsTUFBTSxVQUFVLEdBQUcsTUFBTSxJQUFBLDJCQUFLLEVBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNsRixJQUFJLFVBQVUsQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxLQUFLLGNBQWMsRUFBRTtRQUNyRCxNQUFNLElBQUksS0FBSyxDQUFDLHVCQUF1QixVQUFVLEVBQUUsQ0FBQyxDQUFDO0tBQ3JEO0FBQ0YsQ0FBQztBQUVELElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxNQUFNLEVBQUU7SUFDNUIsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEVBQUU7UUFDakMsT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUNuQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2pCLENBQUMsQ0FBQyxDQUFDO0NBQ0gifQ== \ No newline at end of file diff --git a/build/darwin/create-universal-app.ts b/build/darwin/create-universal-app.ts index 7b10af8af66..ffba8952cd8 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -51,7 +51,7 @@ async function main(buildDir?: string) { fs.writeFileSync(productJsonPath, JSON.stringify(productJson, null, '\t')); // Verify if native module architecture is correct - const findOutput = await spawn('find', [outAppPath, '-name', 'keytar.node']); + const findOutput = await spawn('find', [outAppPath, '-name', 'kerberos.node']); const lipoOutput = await spawn('lipo', ['-archs', findOutput.replace(/\n$/, '')]); if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') { throw new Error(`Invalid arch, got : ${lipoOutput}`); diff --git a/extensions/github-authentication/src/common/keychain.ts b/extensions/github-authentication/src/common/keychain.ts index c7b36d96212..977dade571b 100644 --- a/extensions/github-authentication/src/common/keychain.ts +++ b/extensions/github-authentication/src/common/keychain.ts @@ -3,8 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// keytar depends on a native module shipped in vscode, so this is -// how we load it import * as vscode from 'vscode'; import { Log } from './logger'; diff --git a/extensions/microsoft-authentication/extension-browser.webpack.config.js b/extensions/microsoft-authentication/extension-browser.webpack.config.js index c32a44124a2..2513c7d0f9c 100644 --- a/extensions/microsoft-authentication/extension-browser.webpack.config.js +++ b/extensions/microsoft-authentication/extension-browser.webpack.config.js @@ -20,9 +20,6 @@ module.exports = withBrowserDefaults({ entry: { extension: './src/extension.ts', }, - externals: { - 'keytar': 'commonjs keytar', - }, resolve: { alias: { './node/crypto': path.resolve(__dirname, 'src/browser/crypto'), diff --git a/extensions/microsoft-authentication/extension.webpack.config.js b/extensions/microsoft-authentication/extension.webpack.config.js index a513ac5c3b5..45600607fc5 100644 --- a/extensions/microsoft-authentication/extension.webpack.config.js +++ b/extensions/microsoft-authentication/extension.webpack.config.js @@ -12,9 +12,6 @@ const withDefaults = require('../shared.webpack.config'); module.exports = withDefaults({ context: __dirname, entry: { - extension: './src/extension.ts', - }, - externals: { - 'keytar': 'commonjs keytar' + extension: './src/extension.ts' } }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/languagedetection.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/languagedetection.test.ts index ce6f675b5f6..900eb25649e 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/languagedetection.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/languagedetection.test.ts @@ -36,7 +36,6 @@ suite('vscode - automatic language detection', () => { "outDir": "../out/vs", "target": "es2020", "types": [ - "keytar", "mocha", "semver", "sinon", diff --git a/package.json b/package.json index 938b08591f5..bf70dc18097 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,6 @@ "https-proxy-agent": "^2.2.3", "jschardet": "3.0.0", "kerberos": "^2.0.1", - "keytar": "7.9.0", "minimist": "^1.2.6", "native-is-elevated": "0.7.0", "native-keymap": "^3.3.4", @@ -118,7 +117,6 @@ "@types/gulp-svgmin": "^1.2.1", "@types/http-proxy-agent": "^2.0.1", "@types/kerberos": "^1.1.2", - "@types/keytar": "^4.4.0", "@types/minimist": "^1.2.1", "@types/mocha": "^9.1.1", "@types/node": "18.x", diff --git a/remote/package.json b/remote/package.json index 8fe0e55c1de..f064462fc27 100644 --- a/remote/package.json +++ b/remote/package.json @@ -19,7 +19,6 @@ "https-proxy-agent": "^2.2.3", "jschardet": "3.0.0", "kerberos": "^2.0.1", - "keytar": "7.9.0", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta1", diff --git a/remote/yarn.lock b/remote/yarn.lock index 4716df355d5..e2a07ab9843 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -141,29 +141,6 @@ agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -are-we-there-yet@~1.1.2: - version "1.1.7" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146" - integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -210,26 +187,11 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - cookie@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - debug@3.1.0, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -263,21 +225,11 @@ deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= - detect-libc@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -326,20 +278,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - github-from-package@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" @@ -350,11 +288,6 @@ graceful-fs@4.2.11: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -400,7 +333,7 @@ ieee754@^1.1.13: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -420,18 +353,6 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs= - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -444,11 +365,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - jschardet@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" @@ -463,14 +379,6 @@ kerberos@^2.0.1: node-addon-api "^4.3.0" prebuild-install "7.1.1" -keytar@7.9.0: - version "7.9.0" - resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb" - integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ== - dependencies: - node-addon-api "^4.3.0" - prebuild-install "^7.0.1" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -567,26 +475,6 @@ node-pty@1.1.0-beta1: dependencies: nan "^2.17.0" -npmlog@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= - -object-assign@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -622,30 +510,6 @@ prebuild-install@7.1.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" -prebuild-install@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870" - integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg== - dependencies: - detect-libc "^2.0.0" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^1.0.1" - node-abi "^3.3.0" - npmlog "^4.0.1" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^4.0.0" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -669,19 +533,6 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -readable-stream@^2.0.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - readable-stream@^3.1.1, readable-stream@^3.4.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -696,11 +547,6 @@ safe-buffer@^5.0.1, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - semver@^7.3.5: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" @@ -708,16 +554,6 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" -set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -signal-exit@^3.0.0: - version "3.0.6" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" - integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== - simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -754,24 +590,6 @@ socks@^2.7.1: ip "^2.0.0" smart-buffer "^4.2.0" -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -"string-width@^1.0.2 || 2 || 3 || 4": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -779,27 +597,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -845,7 +642,7 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -util-deprecate@^1.0.1, util-deprecate@~1.0.1: +util-deprecate@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -865,13 +662,6 @@ vscode-textmate@9.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-9.0.0.tgz#313c6c8792b0507aef35aeb81b6b370b37c44d6c" integrity sha512-Cl65diFGxz7gpwbav10HqiY/eVYTO1sjQpmRmV991Bj7wAoOAjGQ97PpQcXorDE2Uc4hnGWLY17xme+5t6MlSg== -wide-align@^1.1.0: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/src/tsconfig.json b/src/tsconfig.json index df96ed71247..56ca209276b 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -8,7 +8,6 @@ "resolveJsonModule": true, "outDir": "../out/vs", "types": [ - "keytar", "mocha", "semver", "sinon", diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 49f3c2703cd..9fcc3ce6f10 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -31,7 +31,6 @@ import { localize } from 'vs/nls'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials'; import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/electron-main/extensionHostDebugIpc'; import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnostics'; import { DiagnosticsMainService, IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService'; @@ -97,7 +96,6 @@ import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; 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'; import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; @@ -964,9 +962,6 @@ export class CodeApplication extends Disposable { // Native Host services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService, undefined, false /* proxied to other processes */)); - // Credentials - services.set(ICredentialsMainService, new SyncDescriptor(CredentialsNativeMainService)); - // Webview Manager services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService)); @@ -1095,10 +1090,6 @@ export class CodeApplication extends Disposable { const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService)); mainProcessElectronServer.registerChannel('encryption', encryptionChannel); - // Credentials - const credentialsChannel = ProxyChannel.fromService(accessor.get(ICredentialsMainService)); - mainProcessElectronServer.registerChannel('credentials', credentialsChannel); - // Signing const signChannel = ProxyChannel.fromService(accessor.get(ISignService)); mainProcessElectronServer.registerChannel('sign', signChannel); diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index 9e6793fd489..bf2a99f601c 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -9,10 +9,8 @@ import { Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; -import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; import { ILogService } from 'vs/platform/log/common/log'; -import { IProductService } from 'vs/platform/product/common/productService'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IApplicationStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; @@ -58,7 +56,6 @@ enum ProxyAuthState { export class ProxyAuthHandler extends Disposable { - private readonly OLD_PROXY_CREDENTIALS_SERVICE_KEY = `${this.productService.urlProtocol}.proxy-credentials`; private readonly PROXY_CREDENTIALS_SERVICE_KEY = 'proxy-credentials://'; private pendingProxyResolve: Promise | undefined = undefined; @@ -70,10 +67,8 @@ export class ProxyAuthHandler extends Disposable { constructor( @ILogService private readonly logService: ILogService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @ICredentialsMainService private readonly credentialsService: ICredentialsMainService, @IEncryptionMainService private readonly encryptionMainService: IEncryptionMainService, - @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, - @IProductService private readonly productService: IProductService + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService ) { super(); @@ -145,34 +140,6 @@ export class ProxyAuthHandler extends Disposable { return undefined; } - // TODO: remove this migration in a release or two. - private async getAndMigrateProxyCredentials(authInfoHash: string): Promise<{ storedUsername: string | undefined; storedPassword: string | undefined }> { - // Find any previously stored credentials - try { - let encryptedSerializedProxyCredentials = this.applicationStorageMainService.get(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION); - let decryptedSerializedProxyCredentials: string | undefined; - if (!encryptedSerializedProxyCredentials) { - encryptedSerializedProxyCredentials = await this.credentialsService.getPassword(this.OLD_PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash) ?? undefined; - if (encryptedSerializedProxyCredentials) { - // re-encrypt to force new encryption algorithm to apply - decryptedSerializedProxyCredentials = await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials); - encryptedSerializedProxyCredentials = await this.encryptionMainService.encrypt(decryptedSerializedProxyCredentials); - this.applicationStorageMainService.store(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, encryptedSerializedProxyCredentials, StorageScope.APPLICATION, StorageTarget.MACHINE); - // Remove it from the old location since it's in the new location. - await this.credentialsService.deletePassword(this.OLD_PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); - } - } - if (encryptedSerializedProxyCredentials) { - const credentials: Credentials = JSON.parse(decryptedSerializedProxyCredentials ?? await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials)); - - return { storedUsername: credentials.username, storedPassword: credentials.password }; - } - } catch (error) { - this.logService.error(error); // handle errors by asking user for login via dialog - } - return { storedUsername: undefined, storedPassword: undefined }; - } - private async doResolveProxyCredentials(authInfo: AuthInfo): Promise { this.logService.trace('auth#doResolveProxyCredentials - enter', authInfo); @@ -181,7 +148,20 @@ export class ProxyAuthHandler extends Disposable { // given the properties of the auth request // (see https://github.com/microsoft/vscode/issues/109497) const authInfoHash = String(hash({ scheme: authInfo.scheme, host: authInfo.host, port: authInfo.port })); - const { storedUsername, storedPassword } = await this.getAndMigrateProxyCredentials(authInfoHash); + + let storedUsername: string | undefined; + let storedPassword: string | undefined; + try { + // Try to find stored credentials for the given auth info + const encryptedValue = this.applicationStorageMainService.get(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION); + if (encryptedValue) { + const credentials: Credentials = JSON.parse(await this.encryptionMainService.decrypt(encryptedValue)); + storedUsername = credentials.username; + storedPassword = credentials.password; + } + } catch (error) { + this.logService.error(error); // handle errors by asking user for login via dialog + } // Reply with stored credentials unless we used them already. // In that case we need to show a login dialog again because @@ -230,7 +210,13 @@ export class ProxyAuthHandler extends Disposable { try { if (reply.remember) { const encryptedSerializedCredentials = await this.encryptionMainService.encrypt(JSON.stringify(credentials)); - this.applicationStorageMainService.store(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, encryptedSerializedCredentials, StorageScope.APPLICATION, StorageTarget.MACHINE); + this.applicationStorageMainService.store( + this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, + encryptedSerializedCredentials, + StorageScope.APPLICATION, + // Always store in machine scope because we do not want these values to be synced + StorageTarget.MACHINE + ); } else { this.applicationStorageMainService.remove(this.PROXY_CREDENTIALS_SERVICE_KEY + authInfoHash, StorageScope.APPLICATION); } diff --git a/src/vs/platform/credentials/common/credentials.ts b/src/vs/platform/credentials/common/credentials.ts deleted file mode 100644 index 889b0a2ac12..00000000000 --- a/src/vs/platform/credentials/common/credentials.ts +++ /dev/null @@ -1,84 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Event } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const ICredentialsService = createDecorator('credentialsService'); - -export interface ICredentialsProvider { - getPassword(service: string, account: string): Promise; - setPassword(service: string, account: string, password: string): Promise; - deletePassword(service: string, account: string): Promise; - findPassword(service: string): Promise; - findCredentials(service: string): Promise>; - clear?(): Promise; -} - -export interface ICredentialsChangeEvent { - service?: string; - account: string; -} - -export interface ICredentialsService extends ICredentialsProvider { - readonly _serviceBrand: undefined; - readonly onDidChangePassword: Event; - - /* - * Each CredentialsService must provide a prefix that will be used - * by the SecretStorage API when storing secrets. - * This is a method that returns a Promise so that it can be defined in - * the main process and proxied on the renderer side. - */ - getSecretStoragePrefix(): Promise; -} - -export const ICredentialsMainService = createDecorator('credentialsMainService'); - -export interface ICredentialsMainService extends ICredentialsService { } - -interface ISecretVault { - [service: string]: { [account: string]: string } | undefined; -} - -export class InMemoryCredentialsProvider implements ICredentialsProvider { - private secretVault: ISecretVault = {}; - - async getPassword(service: string, account: string): Promise { - return this.secretVault[service]?.[account] ?? null; - } - - async setPassword(service: string, account: string, password: string): Promise { - this.secretVault[service] = this.secretVault[service] ?? {}; - this.secretVault[service]![account] = password; - } - - async deletePassword(service: string, account: string): Promise { - if (!this.secretVault[service]?.[account]) { - return false; - } - delete this.secretVault[service]![account]; - if (Object.keys(this.secretVault[service]!).length === 0) { - delete this.secretVault[service]; - } - return true; - } - - async findPassword(service: string): Promise { - return JSON.stringify(this.secretVault[service]) ?? null; - } - - async findCredentials(service: string): Promise> { - const credentials: { account: string; password: string }[] = []; - for (const account of Object.keys(this.secretVault[service] || {})) { - credentials.push({ account, password: this.secretVault[service]![account] }); - } - return credentials; - } - - async clear(): Promise { - this.secretVault = {}; - } -} diff --git a/src/vs/platform/credentials/common/credentialsMainService.ts b/src/vs/platform/credentials/common/credentialsMainService.ts deleted file mode 100644 index 80e3c89b3dd..00000000000 --- a/src/vs/platform/credentials/common/credentialsMainService.ts +++ /dev/null @@ -1,264 +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 { ICredentialsChangeEvent, ICredentialsMainService, InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials'; -import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { ILogService } from 'vs/platform/log/common/log'; -import { isWindows } from 'vs/base/common/platform'; -import { retry, SequencerByKey } from 'vs/base/common/async'; - -interface ChunkedPassword { - content: string; - hasNextChunk: boolean; -} - -export type KeytarModule = typeof import('keytar'); - -export abstract class BaseCredentialsMainService extends Disposable implements ICredentialsMainService { - - private static readonly MAX_PASSWORD_LENGTH = 2500; - private static readonly PASSWORD_CHUNK_SIZE = BaseCredentialsMainService.MAX_PASSWORD_LENGTH - 100; - declare readonly _serviceBrand: undefined; - - private _onDidChangePassword: Emitter = this._register(new Emitter()); - readonly onDidChangePassword = this._onDidChangePassword.event; - - protected _keytarCache: KeytarModule | undefined; - - private _sequencer = new SequencerByKey(); - - constructor( - @ILogService protected readonly logService: ILogService, - ) { - super(); - } - - //#region abstract - - public abstract getSecretStoragePrefix(): Promise; - protected abstract withKeytar(): Promise; - /** - * An optional method that subclasses can implement to assist in surfacing - * Keytar load errors to the user in a friendly way. - */ - protected abstract surfaceKeytarLoadError?: (err: any) => void; - - //#endregion - - async getPassword(service: string, account: string): Promise { - this.logService.trace('Going to get password from keytar:', service, account); - let keytar: KeytarModule; - try { - keytar = await this.withKeytar(); - } catch (e) { - // for get operations, we don't want to surface errors to the user - return null; - } - - return await this._sequencer.queue(service + account, () => this.doGetPassword(keytar, service, account)); - } - - private async doGetPassword(keytar: KeytarModule, service: string, account: string): Promise { - this.logService.trace('Doing get password from keytar:', service, account); - const password = await retry(() => keytar.getPassword(service, account), 50, 3); - if (!password) { - this.logService.trace('Did not get a password from keytar for account:', account); - return password; - } - - let content: string | undefined; - let hasNextChunk: boolean | undefined; - try { - const parsed: ChunkedPassword = JSON.parse(password); - content = parsed.content; - hasNextChunk = parsed.hasNextChunk; - } catch { - // Ignore this similar to how we ignore parse errors in the delete - // because on non-windows this will not be a JSON string. - } - - if (!content || !hasNextChunk) { - this.logService.trace('Got password from keytar for account:', account); - return password; - } - - try { - let index = 1; - while (hasNextChunk) { - const nextChunk = await retry(() => keytar.getPassword(service, `${account}-${index}`), 50, 3); - const result: ChunkedPassword = JSON.parse(nextChunk!); - content += result.content; - hasNextChunk = result.hasNextChunk; - index++; - } - - this.logService.trace(`Got ${index}-chunked password from keytar for account:`, account); - return content; - } catch (e) { - this.logService.error(e); - return password; - } - } - - async setPassword(service: string, account: string, password: string): Promise { - this.logService.trace('Going to set password using keytar:', service, account); - let keytar: KeytarModule; - try { - keytar = await this.withKeytar(); - } catch (e) { - this.surfaceKeytarLoadError?.(e); - throw e; - } - - await this._sequencer.queue(service + account, () => this.doSetPassword(keytar, service, account, password)); - this._onDidChangePassword.fire({ service, account }); - } - - private async doSetPassword(keytar: KeytarModule, service: string, account: string, password: string): Promise { - this.logService.trace('Doing set password from keytar:', service, account); - if (!isWindows) { - await retry(() => keytar.setPassword(service, account, password), 50, 3); - this.logService.trace('Set password from keytar for account:', account); - return; - } - - // On Windows, we sometimes have to chunk the password because the Windows Credential Manager only allows passwords of a max length. - // So to make sure we can store passwords of any length, we chunk the longer passwords and store it as multiple passwords. - // To ensure we store any password correctly, we first delete any existing password, chunks and all, and then store the new ones. - - await this.doDeletePassword(keytar, service, account); - - // if it's a short password, just store it - if (password.length <= BaseCredentialsMainService.PASSWORD_CHUNK_SIZE) { - await retry(() => keytar.setPassword(service, account, password), 50, 3); - this.logService.trace('Set password from keytar for account:', account); - return; - } - - // otherwise, chunk it and store it - let index = 0; - let chunk = 0; - let hasNextChunk = true; - while (hasNextChunk) { - const passwordChunk = password.substring(index, index + BaseCredentialsMainService.PASSWORD_CHUNK_SIZE); - index += BaseCredentialsMainService.PASSWORD_CHUNK_SIZE; - hasNextChunk = password.length - index > 0; - - const content: ChunkedPassword = { - content: passwordChunk, - hasNextChunk: hasNextChunk - }; - await retry(() => keytar.setPassword(service, chunk ? `${account}-${chunk}` : account, JSON.stringify(content)), 50, 3); - chunk++; - } - - this.logService.trace(`Set${chunk ? ` ${chunk}-chunked` : ''} password from keytar for account:`, account); - } - - async deletePassword(service: string, account: string): Promise { - this.logService.trace('Going to delete password using keytar:', service, account); - let keytar: KeytarModule; - try { - keytar = await this.withKeytar(); - } catch (e) { - this.surfaceKeytarLoadError?.(e); - throw e; - } - - const result = await this._sequencer.queue(service + account, () => this.doDeletePassword(keytar, service, account)); - if (result) { - this._onDidChangePassword.fire({ service, account }); - } - return result; - } - - private async doDeletePassword(keytar: KeytarModule, service: string, account: string): Promise { - this.logService.trace('Doing delete password from keytar:', service, account); - const password = await keytar.getPassword(service, account); - if (!password) { - this.logService.trace('Did not get a password to delete from keytar for account:', account); - return false; - } - - let content: string | undefined; - let hasNextChunk: boolean | undefined; - try { - const possibleChunk = JSON.parse(password); - content = possibleChunk.content; - hasNextChunk = possibleChunk.hasNextChunk; - } 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 - } - - let index = 0; - if (content && hasNextChunk) { - try { - // need to delete additional chunks - index++; - 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++; - } - } catch (e) { - this.logService.error(e); - } - } - - // Delete the first account to determine deletion success - if (await keytar.deletePassword(service, account)) { - this.logService.trace(`Deleted${index ? ` ${index}-chunked` : ''} password from keytar for account:`, account); - return true; - } - - this.logService.trace(`Keytar failed to delete${index ? ` ${index}-chunked` : ''} password for account:`, account); - return false; - } - - async findPassword(service: string): Promise { - let keytar: KeytarModule; - try { - keytar = await this.withKeytar(); - } catch (e) { - // for get operations, we don't want to surface errors to the user - return null; - } - - return await keytar.findPassword(service); - } - - async findCredentials(service: string): Promise> { - let keytar: KeytarModule; - try { - keytar = await this.withKeytar(); - } catch (e) { - // for get operations, we don't want to surface errors to the user - return []; - } - - return await keytar.findCredentials(service); - } - - public clear(): Promise { - if (this._keytarCache instanceof InMemoryCredentialsProvider) { - return this._keytarCache.clear(); - } - - // We don't know how to properly clear Keytar because we don't know - // what services have stored credentials. For reference, a "service" is an extension. - // TODO: should we clear credentials for the built-in auth extensions? - return Promise.resolve(); - } -} diff --git a/src/vs/platform/credentials/electron-main/credentialsMainService.ts b/src/vs/platform/credentials/electron-main/credentialsMainService.ts deleted file mode 100644 index f62719408c1..00000000000 --- a/src/vs/platform/credentials/electron-main/credentialsMainService.ts +++ /dev/null @@ -1,49 +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 { InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials'; -import { ILogService } from 'vs/platform/log/common/log'; -import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; -import { BaseCredentialsMainService, KeytarModule } from 'vs/platform/credentials/common/credentialsMainService'; - -export class CredentialsNativeMainService extends BaseCredentialsMainService { - - constructor( - @ILogService logService: ILogService, - @INativeEnvironmentService private readonly environmentMainService: INativeEnvironmentService, - @IProductService private readonly productService: IProductService, - @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - ) { - super(logService); - } - - // If the credentials service is running on the server, we add a suffix -server to differentiate from the location that the - // client would store the credentials. - public override async getSecretStoragePrefix() { return Promise.resolve(this.productService.urlProtocol); } - - protected async withKeytar(): Promise { - if (this._keytarCache) { - return this._keytarCache; - } - - if (this.environmentMainService.disableKeytar) { - this.logService.info('Keytar is disabled. Using in-memory credential store instead.'); - this._keytarCache = new InMemoryCredentialsProvider(); - return this._keytarCache; - } - - const keytarCache = await import('keytar'); - // Try using keytar to see if it throws or not. - await keytarCache.findCredentials('test-keytar-loads'); - this._keytarCache = keytarCache; - return this._keytarCache; - } - - protected override surfaceKeytarLoadError = (err: any) => { - this.windowsMainService.sendToFocused('vscode:showCredentialsError', err.message ?? err); - }; -} diff --git a/src/vs/platform/credentials/node/credentialsMainService.ts b/src/vs/platform/credentials/node/credentialsMainService.ts deleted file mode 100644 index cc2156d22cd..00000000000 --- a/src/vs/platform/credentials/node/credentialsMainService.ts +++ /dev/null @@ -1,51 +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 { InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials'; -import { ILogService } from 'vs/platform/log/common/log'; -import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { BaseCredentialsMainService, KeytarModule } from 'vs/platform/credentials/common/credentialsMainService'; - -export class CredentialsWebMainService extends BaseCredentialsMainService { - // Since we fallback to the in-memory credentials provider, we do not need to surface any Keytar load errors - // to the user. - protected surfaceKeytarLoadError?: (err: any) => void; - - constructor( - @ILogService logService: ILogService, - @INativeEnvironmentService private readonly environmentMainService: INativeEnvironmentService, - @IProductService private readonly productService: IProductService, - ) { - super(logService); - } - - // If the credentials service is running on the server, we add a suffix -server to differentiate from the location that the - // client would store the credentials. - public override async getSecretStoragePrefix() { return Promise.resolve(`${this.productService.urlProtocol}-server`); } - - protected async withKeytar(): Promise { - if (this._keytarCache) { - return this._keytarCache; - } - - if (this.environmentMainService.disableKeytar) { - this.logService.info('Keytar is disabled. Using in-memory credential store instead.'); - this._keytarCache = new InMemoryCredentialsProvider(); - return this._keytarCache; - } - - try { - this._keytarCache = await import('keytar'); - // Try using keytar to see if it throws or not. - await this._keytarCache.findCredentials('test-keytar-loads'); - } catch (e) { - this.logService.warn( - `Using the in-memory credential store as the operating system's credential store could not be accessed. Please see https://aka.ms/vscode-server-keyring on how to set this up. Details: ${e.message ?? e}`); - this._keytarCache = new InMemoryCredentialsProvider(); - } - return this._keytarCache; - } -} diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts index 4148b9dc540..68128a214fd 100644 --- a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts +++ b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { isLinux, isWindows } from 'vs/base/common/platform'; +import { isWindows } from 'vs/base/common/platform'; import { flakySuite } from 'vs/base/test/common/testUtils'; function testErrorMessage(module: string): string { @@ -74,28 +74,6 @@ flakySuite('Native Modules (all platforms)', () => { }); }); -(isLinux ? suite.skip : suite)('Native Modules (Windows, macOS)', () => { - - test('keytar', async () => { - const keytar = await import('keytar'); - const name = `VSCode Test ${Math.floor(Math.random() * 1e9)}`; - try { - await keytar.setPassword(name, 'foo', 'bar'); - assert.strictEqual(await keytar.findPassword(name), 'bar'); - assert.strictEqual((await keytar.findCredentials(name)).length, 1); - assert.strictEqual(await keytar.getPassword(name, 'foo'), 'bar'); - await keytar.deletePassword(name, 'foo'); - assert.strictEqual(await keytar.getPassword(name, 'foo'), null); - } catch (err) { - try { - await keytar.deletePassword(name, 'foo'); // try to clean up - } catch { } - - throw err; - } - }); -}); - (!isWindows ? suite.skip : suite)('Native Modules (Windows)', () => { (process.type === 'renderer' ? test.skip /* TODO@electron module is not context aware yet and thus cannot load in Electron renderer used by tests */ : test)('@vscode/windows-mutex', async () => { diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 7e568185997..422370324f7 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -11,12 +11,10 @@ import * as path from 'vs/base/common/path'; import { IURITransformer } from 'vs/base/common/uriIpc'; import { getMachineId } from 'vs/base/node/id'; import { Promises } from 'vs/base/node/pfs'; -import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; +import { ClientConnectionEvent, IMessagePassingProtocol, IPCServer, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; import { ProtocolConstants } from 'vs/base/parts/ipc/common/ipc.net'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; -import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials'; -import { CredentialsWebMainService } from 'vs/platform/credentials/node/credentialsMainService'; import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadServiceChannelClient } from 'vs/platform/download/common/downloadIpc'; @@ -201,8 +199,6 @@ export async function setupServerServices(connectionToken: ServerConnectionToken const ptyHostService = instantiationService.createInstance(PtyHostService, ptyHostStarter); services.set(IPtyService, ptyHostService); - services.set(ICredentialsMainService, new SyncDescriptor(CredentialsWebMainService)); - instantiationService.invokeFunction(accessor => { const extensionManagementService = accessor.get(INativeServerExtensionManagementService); const extensionsScannerService = accessor.get(IExtensionsScannerService); @@ -227,9 +223,6 @@ export async function setupServerServices(connectionToken: ServerConnectionToken const channel = new ExtensionManagementChannel(extensionManagementService, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority)); socketServer.registerChannel('extensions', channel); - const credentialsChannel = ProxyChannel.fromService(accessor.get(ICredentialsMainService)); - socketServer.registerChannel('credentials', credentialsChannel); - // clean up extensions folder remoteExtensionsScanner.whenExtensionsReady().then(() => extensionManagementService.cleanUp()); diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 9217613b50a..abb8b45a8ad 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -42,7 +42,6 @@ import './mainThreadErrors'; import './mainThreadExtensionService'; import './mainThreadFileSystem'; import './mainThreadFileSystemEventService'; -import './mainThreadKeytar'; import './mainThreadLanguageFeatures'; import './mainThreadLanguages'; import './mainThreadLogService'; diff --git a/src/vs/workbench/api/browser/mainThreadKeytar.ts b/src/vs/workbench/api/browser/mainThreadKeytar.ts deleted file mode 100644 index 8b7649ada1f..00000000000 --- a/src/vs/workbench/api/browser/mainThreadKeytar.ts +++ /dev/null @@ -1,41 +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 { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { MainContext, MainThreadKeytarShape } from 'vs/workbench/api/common/extHost.protocol'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; - -@extHostNamedCustomer(MainContext.MainThreadKeytar) -export class MainThreadKeytar implements MainThreadKeytarShape { - - constructor( - _extHostContext: IExtHostContext, - @ICredentialsService private readonly _credentialsService: ICredentialsService, - ) { } - - async $getPassword(service: string, account: string): Promise { - return this._credentialsService.getPassword(service, account); - } - - async $setPassword(service: string, account: string, password: string): Promise { - return this._credentialsService.setPassword(service, account, password); - } - - async $deletePassword(service: string, account: string): Promise { - return this._credentialsService.deletePassword(service, account); - } - - async $findPassword(service: string): Promise { - return this._credentialsService.findPassword(service); - } - - async $findCredentials(service: string): Promise> { - return this._credentialsService.findCredentials(service); - } - - dispose(): void { - // - } -} diff --git a/src/vs/workbench/api/browser/mainThreadSecretState.ts b/src/vs/workbench/api/browser/mainThreadSecretState.ts index d8fa95ac538..2c70d06f1ac 100644 --- a/src/vs/workbench/api/browser/mainThreadSecretState.ts +++ b/src/vs/workbench/api/browser/mainThreadSecretState.ts @@ -5,146 +5,28 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { ExtHostContext, ExtHostSecretStateShape, MainContext, MainThreadSecretStateShape } from '../common/extHost.protocol'; import { ILogService } from 'vs/platform/log/common/log'; import { SequencerByKey } from 'vs/base/common/async'; import { ISecretStorageService } from 'vs/platform/secrets/common/secrets'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; -import { IEncryptionService } from 'vs/platform/encryption/common/encryptionService'; - -class OldMainThreadSecretState extends Disposable implements MainThreadSecretStateShape { - - private secretStoragePrefix = this.credentialsService.getSecretStoragePrefix(); - - constructor( - private readonly _proxy: ExtHostSecretStateShape, - private readonly credentialsService: ICredentialsService, - private readonly encryptionService: IEncryptionService, - private readonly logService: ILogService, - ) { - super(); - this._register(this.credentialsService.onDidChangePassword(async e => { - const extensionId = e.service?.substring((await this.secretStoragePrefix).length); - if (extensionId) { - this._proxy.$onDidChangePassword({ extensionId, key: e.account }); - } - })); - } - - private async getFullKey(extensionId: string): Promise { - return `${await this.secretStoragePrefix}${extensionId}`; - } - - async $getPassword(extensionId: string, key: string): Promise { - this.logService.trace(`MainThreadSecretState#getPassword: Getting password for ${extensionId} extension: `, key); - const fullKey = await this.getFullKey(extensionId); - const password = await this.credentialsService.getPassword(fullKey, key); - if (!password) { - this.logService.trace('MainThreadSecretState#getPassword: No password found for: ', key); - return undefined; - } - - let decrypted: string | null; - try { - this.logService.trace('MainThreadSecretState#getPassword: Decrypting password for: ', key); - decrypted = await this.encryptionService.decrypt(password); - } catch (e) { - this.logService.error(e); - this.logService.trace('MainThreadSecretState#getPassword: Trying migration for: ', key); - - // If we are on a platform that newly started encrypting secrets before storing them, - // then passwords previously stored were stored un-encrypted (NOTE: but still being stored in a secure keyring). - // When we try to decrypt a password that wasn't encrypted previously, the encryption service will throw. - // To recover gracefully, we first try to encrypt & store the password (essentially migrating the secret to the new format) - // and then we try to read it and decrypt again. - const encryptedForSet = await this.encryptionService.encrypt(password); - await this.credentialsService.setPassword(fullKey, key, encryptedForSet); - const passwordEncrypted = await this.credentialsService.getPassword(fullKey, key); - decrypted = passwordEncrypted && await this.encryptionService.decrypt(passwordEncrypted); - } - - if (decrypted) { - try { - const value = JSON.parse(decrypted); - if (value.extensionId === extensionId) { - this.logService.trace('MainThreadSecretState#getPassword: Password found for: ', key); - return value.content; - } - } catch (parseError) { - // We may not be able to parse it, but we keep the secret in the keychain anyway just in case - // it decrypts correctly in the future. - this.logService.error(parseError); - throw new Error('Unable to parse decrypted password'); - } - } - - this.logService.trace('MainThreadSecretState#getPassword: No password found for: ', key); - return undefined; - } - - async $setPassword(extensionId: string, key: string, value: string): Promise { - this.logService.trace(`MainThreadSecretState#setPassword: Setting password for ${extensionId} extension: `, key); - const fullKey = await this.getFullKey(extensionId); - const toEncrypt = JSON.stringify({ - extensionId, - content: value - }); - this.logService.trace('MainThreadSecretState#setPassword: Encrypting password for: ', key); - const encrypted = await this.encryptionService.encrypt(toEncrypt); - this.logService.trace('MainThreadSecretState#setPassword: Storing password for: ', key); - return await this.credentialsService.setPassword(fullKey, key, encrypted); - } - - async $deletePassword(extensionId: string, key: string): Promise { - try { - const fullKey = await this.getFullKey(extensionId); - await this.credentialsService.deletePassword(fullKey, key); - } catch (_) { - throw new Error('Cannot delete password'); - } - } -} @extHostNamedCustomer(MainContext.MainThreadSecretState) export class MainThreadSecretState extends Disposable implements MainThreadSecretStateShape { private readonly _proxy: ExtHostSecretStateShape; - // TODO: Remove this when all known embedders implement a secret storage provider - private readonly _oldMainThreadSecretState: OldMainThreadSecretState | undefined; - private readonly _sequencer = new SequencerByKey(); - // TODO: Remove this when we remove the old API - private secretStoragePrefix = this.credentialsService.getSecretStoragePrefix(); - constructor( extHostContext: IExtHostContext, @ISecretStorageService private readonly secretStorageService: ISecretStorageService, @ILogService private readonly logService: ILogService, - // TODO: Remove this when we remove the old API - @ICredentialsService private readonly credentialsService: ICredentialsService, - // TODO: Remove this when we remove the old API - @IEncryptionService private readonly encryptionService: IEncryptionService, - @IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService, + @IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService ) { super(); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSecretState); - // If the embedder doesn't implement a secret storage provider, then we need to use the old API - // to ensure that secrets are still stored in a secure way. This is only temporary until all - // embedders implement a secret storage provider. - // TODO: Remove this when all known embedders implement a secret storage provider - if (environmentService.options?.credentialsProvider && !environmentService.options?.secretStorageProvider) { - this._oldMainThreadSecretState = this._register(new OldMainThreadSecretState( - this._proxy, - credentialsService, - encryptionService, - logService - )); - } - this._register(this.secretStorageService.onDidChangeSecret((e: string) => { try { const { extensionId, key } = this.parseKey(e); @@ -163,23 +45,9 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre } private async doGetPassword(extensionId: string, key: string): Promise { - // TODO: Remove this when all known embedders implement a secret storage provider - if (this._oldMainThreadSecretState) { - return await this._oldMainThreadSecretState.$getPassword(extensionId, key); - } - const fullKey = this.getKey(extensionId, key); - const password = await this.secretStorageService.get(fullKey); - if (!password) { - this.logService.trace('[mainThreadSecretState] No password found for: ', extensionId, key); - - // TODO: Remove this when we remove the old API - const password = await this.getAndDeleteOldPassword(extensionId, key); - return password; - } - - this.logService.trace('[mainThreadSecretState] Password found for: ', extensionId, key); + this.logService.trace(`[mainThreadSecretState] ${password ? 'P' : 'No p'}assword found for: `, extensionId, key); return password; } @@ -189,11 +57,6 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre } private async doSetPassword(extensionId: string, key: string, value: string): Promise { - // TODO: Remove this when all known embedders implement a secret storage provider - if (this._oldMainThreadSecretState) { - return await this._oldMainThreadSecretState.$setPassword(extensionId, key, value); - } - const fullKey = this.getKey(extensionId, key); await this.secretStorageService.set(fullKey, value); this.logService.trace('[mainThreadSecretState] Password set for: ', extensionId, key); @@ -205,11 +68,6 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre } private async doDeletePassword(extensionId: string, key: string): Promise { - // TODO: Remove this when all known embedders implement a secret storage provider - if (this._oldMainThreadSecretState) { - return await this._oldMainThreadSecretState.$deletePassword(extensionId, key); - } - const fullKey = this.getKey(extensionId, key); await this.secretStorageService.delete(fullKey); this.logService.trace('[mainThreadSecretState] Password deleted for: ', extensionId, key); @@ -222,83 +80,4 @@ export class MainThreadSecretState extends Disposable implements MainThreadSecre private parseKey(key: string): { extensionId: string; key: string } { return JSON.parse(key); } - - //#region Old API - - // Delete this all when we remove the old API - - private async getAndDeleteOldPassword(extensionId: string, key: string): Promise { - const password = await this.getOldPassword(extensionId, key); - if (password) { - const fullKey = this.getKey(extensionId, key); - this.logService.trace('[mainThreadSecretState] Setting old password to new location for: ', extensionId, key); - await this.secretStorageService.set(fullKey, password); - this.logService.trace('[mainThreadSecretState] Old Password set to new location for: ', extensionId, key); - if (this.secretStorageService.type === 'persisted') { - this.logService.trace('[mainThreadSecretState] Deleting old password for since it was persisted in the new location: ', extensionId, key); - await this.deleteOldPassword(extensionId, key); - } - } - return password; - } - - private async getOldPassword(extensionId: string, key: string): Promise { - this.logService.trace(`[mainThreadSecretState] Getting old password for ${extensionId} extension: `, key); - const fullKey = `${await this.secretStoragePrefix}${extensionId}`; - const password = await this.credentialsService.getPassword(fullKey, key); - if (!password) { - this.logService.trace('[mainThreadSecretState] No old password found for: ', extensionId, key); - return undefined; - } - - let decrypted: string | null; - try { - this.logService.trace('[mainThreadSecretState] Decrypting old password for: ', extensionId, key); - decrypted = await this.encryptionService.decrypt(password); - } catch (e) { - this.logService.error(e); - this.logService.trace('[mainThreadSecretState] Trying old migration for: ', extensionId, key); - - // If we are on a platform that newly started encrypting secrets before storing them, - // then passwords previously stored were stored un-encrypted (NOTE: but still being stored in a secure keyring). - // When we try to decrypt a password that wasn't encrypted previously, the encryption service will throw. - // To recover gracefully, we first try to encrypt & store the password (essentially migrating the secret to the new format) - // and then we try to read it and decrypt again. - const encryptedForSet = await this.encryptionService.encrypt(password); - await this.credentialsService.setPassword(fullKey, key, encryptedForSet); - const passwordEncrypted = await this.credentialsService.getPassword(fullKey, key); - decrypted = passwordEncrypted && await this.encryptionService.decrypt(passwordEncrypted); - } - - if (decrypted) { - try { - const value = JSON.parse(decrypted); - if (value.extensionId === extensionId) { - this.logService.trace('[mainThreadSecretState] Old password found for: ', extensionId, key); - return value.content; - } - } catch (parseError) { - // We may not be able to parse it, but we keep the secret in the keychain anyway just in case - // it decrypts correctly in the future. - this.logService.error(parseError); - return undefined; - } - } - - this.logService.trace('[mainThreadSecretState] No old password found for: ', extensionId, key); - return undefined; - } - - private async deleteOldPassword(extensionId: string, key: string): Promise { - try { - const fullKey = `${await this.secretStoragePrefix}${extensionId}`; - this.logService.trace(`[mainThreadSecretState] Deleting old password for ${extensionId} extension: `, key); - await this.credentialsService.deletePassword(fullKey, key); - this.logService.trace('[mainThreadSecretState] Old password deleted for: ', extensionId, key); - } catch (_) { - throw new Error('Cannot delete password'); - } - } - - //#endregion } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 3c894ef8694..a3063e71595 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -298,14 +298,6 @@ export interface MainThreadConsoleShape extends IDisposable { $logExtensionHostMessage(msg: IRemoteConsoleLog): void; } -export interface MainThreadKeytarShape extends IDisposable { - $getPassword(service: string, account: string): Promise; - $setPassword(service: string, account: string, password: string): Promise; - $deletePassword(service: string, account: string): Promise; - $findPassword(service: string): Promise; - $findCredentials(service: string): Promise>; -} - export interface IRegExpDto { pattern: string; flags?: string; @@ -2631,7 +2623,6 @@ export const MainContext = { MainThreadErrors: createProxyIdentifier('MainThreadErrors'), MainThreadTreeViews: createProxyIdentifier('MainThreadTreeViews'), MainThreadDownloadService: createProxyIdentifier('MainThreadDownloadService'), - MainThreadKeytar: createProxyIdentifier('MainThreadKeytar'), MainThreadLanguageFeatures: createProxyIdentifier('MainThreadLanguageFeatures'), MainThreadLanguages: createProxyIdentifier('MainThreadLanguages'), MainThreadLogger: createProxyIdentifier('MainThreadLogger'), diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index fb96819767b..bcf03a6a02a 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -15,7 +15,6 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ExtensionPaths, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; -import { platform } from 'vs/base/common/process'; import { ILogService } from 'vs/platform/log/common/log'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; @@ -61,7 +60,6 @@ export abstract class RequireInterceptor { const extensionPaths = await this._extHostExtensionService.getExtensionPathIndex(); this.register(new VSCodeNodeModuleFactory(this._apiFactory, extensionPaths, this._extensionRegistry, configProvider, this._logService)); - this.register(this._instaService.createInstance(KeytarNodeModuleFactory, extensionPaths)); this.register(this._instaService.createInstance(NodeModuleAliasingModuleFactory)); if (this._initData.remote.isRemote) { this.register(this._instaService.createInstance(OpenNodeModuleFactory, extensionPaths, this._initData.environment.appUriScheme)); @@ -187,97 +185,6 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory { //#endregion - -//#region --- keytar-module - -interface IKeytarModule { - getPassword(service: string, account: string): Promise; - setPassword(service: string, account: string, password: string): Promise; - deletePassword(service: string, account: string): Promise; - findPassword(service: string): Promise; - findCredentials(service: string): Promise>; -} - -class KeytarNodeModuleFactory implements INodeModuleFactory { - public readonly nodeModuleName: string = 'keytar'; - - private readonly _mainThreadTelemetry: MainThreadTelemetryShape; - private alternativeNames: Set | undefined; - private _impl: IKeytarModule; - - constructor( - private readonly _extensionPaths: ExtensionPaths, - @IExtHostRpcService rpcService: IExtHostRpcService, - @IExtHostInitDataService initData: IExtHostInitDataService, - - ) { - this._mainThreadTelemetry = rpcService.getProxy(MainContext.MainThreadTelemetry); - const { environment } = initData; - const mainThreadKeytar = rpcService.getProxy(MainContext.MainThreadKeytar); - - if (environment.appRoot) { - let appRoot = environment.appRoot.fsPath; - if (platform === 'win32') { - appRoot = appRoot.replace(/\\/g, '/'); - } - if (appRoot[appRoot.length - 1] === '/') { - appRoot = appRoot.substr(0, appRoot.length - 1); - } - this.alternativeNames = new Set(); - this.alternativeNames.add(`${appRoot}/node_modules.asar/keytar`); - this.alternativeNames.add(`${appRoot}/node_modules/keytar`); - } - this._impl = { - getPassword: (service: string, account: string): Promise => { - return mainThreadKeytar.$getPassword(service, account); - }, - setPassword: (service: string, account: string, password: string): Promise => { - return mainThreadKeytar.$setPassword(service, account, password); - }, - deletePassword: (service: string, account: string): Promise => { - return mainThreadKeytar.$deletePassword(service, account); - }, - findPassword: (service: string): Promise => { - return mainThreadKeytar.$findPassword(service); - }, - findCredentials(service: string): Promise> { - return mainThreadKeytar.$findCredentials(service); - } - }; - } - - public load(_request: string, parent: URI): any { - const ext = this._extensionPaths.findSubstr(parent); - type ShimmingKeytarClassification = { - owner: 'jrieken'; - comment: 'Know when the keytar-shim was used'; - extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension is question' }; - }; - this._mainThreadTelemetry.$publicLog2<{ extension: string }, ShimmingKeytarClassification>('shimming.keytar', { extension: ext?.identifier.value ?? 'unknown_extension' }); - return this._impl; - } - - public alternativeModuleName(name: string): string | undefined { - const length = name.length; - // We need at least something like: `?/keytar` which requires - // more than 7 characters. - if (length <= 7 || !this.alternativeNames) { - return undefined; - } - const sep = length - 7; - if ((name.charAt(sep) === '/' || name.charAt(sep) === '\\') && name.endsWith('keytar')) { - name = name.replace(/\\/g, '/'); - if (this.alternativeNames.has(name)) { - return 'keytar'; - } - } - return undefined; - } -} - -//#endregion - - //#region --- opn/open-module interface OpenOptions { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 4f70c6103f8..6e8afba3bf5 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -36,7 +36,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IPaneCompositePart } from 'vs/workbench/browser/parts/paneCompositePart'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ILogService } from 'vs/platform/log/common/log'; @@ -234,7 +233,7 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { private readonly problematicProviders: Set = new Set(); private initialized = false; - private sessionFromEmbedder = new Lazy>(() => getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService)); + private sessionFromEmbedder = new Lazy>(() => getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService)); constructor( action: ActivityAction, @@ -254,7 +253,6 @@ export class AccountsActivityActionViewItem extends MenuActivityActionViewItem { @IStorageService private readonly storageService: IStorageService, @IKeybindingService keybindingService: IKeybindingService, @ISecretStorageService private readonly secretStorageService: ISecretStorageService, - @ICredentialsService private readonly credentialsService: ICredentialsService, @ILogService private readonly logService: ILogService ) { super(MenuId.AccountsContext, action, contextMenuActionsProvider, true, colors, activityHoverOptions, themeService, hoverService, menuService, contextMenuService, contextKeyService, configurationService, environmentService, keybindingService); diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index 8295ab26129..1d703966a89 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -12,7 +12,6 @@ import type { IUpdateProvider } from 'vs/workbench/services/update/browser/updat import type { Event } from 'vs/base/common/event'; import type { IWorkspaceProvider } from 'vs/workbench/services/host/browser/browserHostService'; import type { IProductConfiguration } from 'vs/base/common/product'; -import type { ICredentialsProvider } from 'vs/platform/credentials/common/credentials'; import type { ISecretStorageProvider } from 'vs/platform/secrets/common/secrets'; import type { TunnelProviderFeatures } from 'vs/platform/tunnel/common/tunnel'; import type { IProgress, IProgressCompositeOptions, IProgressDialogOptions, IProgressNotificationOptions, IProgressOptions, IProgressStep, IProgressWindowOptions } from 'vs/platform/progress/common/progress'; @@ -210,12 +209,6 @@ export interface IWorkbenchConstructionOptions { */ readonly settingsSyncOptions?: ISettingsSyncOptions; - /** - * The credentials provider to store and retrieve secrets. - * TODO: Remove this in favor of the secret storage provider. - */ - readonly credentialsProvider?: ICredentialsProvider; - /** * The secret storage provider to store and retrieve secrets. */ diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 925403791af..b464fe5abac 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -62,9 +62,7 @@ import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService } fr import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { mixin, safeStringify } from 'vs/base/common/objects'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IndexedDB } from 'vs/base/browser/indexedDB'; -import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService'; import { IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; import { WebFileSystemAccess } from 'vs/platform/files/browser/webFileSystemAccess'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -389,10 +387,6 @@ export class BrowserMain extends Disposable { // // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // Credentials Service - const credentialsService = new BrowserCredentialsService(environmentService, remoteAgentService, productService); - serviceCollection.set(ICredentialsService, credentialsService); - const encryptionService = new EncryptionService(); serviceCollection.set(IEncryptionService, encryptionService); const secretStorageService = new BrowserSecretStorageService(storageService, encryptionService, environmentService, logService); @@ -400,7 +394,7 @@ export class BrowserMain extends Disposable { // Userdata Initialize Service const userDataInitializers: IUserDataInitializer[] = []; - userDataInitializers.push(new UserDataSyncInitializer(environmentService, secretStorageService, credentialsService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService)); + userDataInitializers.push(new UserDataSyncInitializer(environmentService, secretStorageService, userDataSyncStoreManagementService, fileService, userDataProfilesService, storageService, productService, requestService, logService, uriIdentityService)); if (environmentService.options.profile) { userDataInitializers.push(new UserDataProfileInitializer(environmentService, fileService, userDataProfileService, storageService, logService, uriIdentityService, requestService)); } @@ -497,7 +491,6 @@ export class BrowserMain extends Disposable { const dialogService = accessor.get(IDialogService); const hostService = accessor.get(IHostService); const storageService = accessor.get(IStorageService); - const credentialsService = accessor.get(ICredentialsService); const logService = accessor.get(ILogService); const result = await dialogService.confirm({ message: localize('reset user data message', "Would you like to reset your data (settings, keybindings, extensions, snippets and UI State) and reload?") @@ -509,9 +502,6 @@ export class BrowserMain extends Disposable { if (storageService instanceof BrowserStorageService) { await storageService.clear(); } - if (typeof credentialsService.clear === 'function') { - await credentialsService.clear(); - } } catch (error) { logService.error(error); throw error; diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts index 040294170b9..00914aa8eff 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts @@ -18,7 +18,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { EDIT_SESSIONS_SIGNED_IN, EditSession, EDIT_SESSION_SYNC_CATEGORY, IEditSessionsStorageService, EDIT_SESSIONS_SIGNED_IN_KEY, IEditSessionsLogService, SyncResource, EDIT_SESSIONS_PENDING_KEY } from 'vs/workbench/contrib/editSessions/common/editSessions'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { generateUuid } from 'vs/base/common/uuid'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import { isWeb } from 'vs/base/common/platform'; import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; @@ -82,8 +81,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes @IProductService private readonly productService: IProductService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IDialogService private readonly dialogService: IDialogService, - @ISecretStorageService private readonly secretStorageService: ISecretStorageService, - @ICredentialsService private readonly credentialsService: ICredentialsService + @ISecretStorageService private readonly secretStorageService: ISecretStorageService ) { super(); @@ -280,7 +278,7 @@ export class EditSessionsWorkbenchService extends Disposable implements IEditSes // If settings sync is already enabled, avoid asking again to authenticate if (this.shouldAttemptEditSessionInit()) { this.logService.info(`Reusing user data sync enablement`); - const authenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); + const authenticationSessionInfo = await getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService); if (authenticationSessionInfo !== undefined) { this.logService.info(`Using current authentication session with ID ${authenticationSessionInfo.id}`); this.existingSessionId = authenticationSessionInfo.id; diff --git a/src/vs/workbench/services/authentication/browser/authenticationService.ts b/src/vs/workbench/services/authentication/browser/authenticationService.ts index f6c2f14685a..7ad844439c5 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationService.ts @@ -13,7 +13,6 @@ import * as nls from 'vs/nls'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { Severity } from 'vs/platform/notification/common/notification'; @@ -80,14 +79,10 @@ export function addAccountUsage(storageService: IStorageService, providerId: str // TODO: pull this out into its own service export type AuthenticationSessionInfo = { readonly id: string; readonly accessToken: string; readonly providerId: string; readonly canSignOut?: boolean }; export async function getCurrentAuthenticationSessionInfo( - // TODO: Remove when all known embedders implement SecretStorageProviders instead of CredentialsProviders - credentialsService: ICredentialsService, secretStorageService: ISecretStorageService, productService: IProductService ): Promise { - const authenticationSessionValue = - await secretStorageService.get(`${productService.urlProtocol}.loginAccount`) - ?? await credentialsService.getPassword(`${productService.urlProtocol}.login`, 'account'); + const authenticationSessionValue = await secretStorageService.get(`${productService.urlProtocol}.loginAccount`); if (authenticationSessionValue) { try { const authenticationSessionInfo: AuthenticationSessionInfo = JSON.parse(authenticationSessionValue); diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts deleted file mode 100644 index 80a551f4cc3..00000000000 --- a/src/vs/workbench/services/credentials/browser/credentialsService.ts +++ /dev/null @@ -1,82 +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 { ICredentialsService, ICredentialsProvider, ICredentialsChangeEvent, InMemoryCredentialsProvider } from 'vs/platform/credentials/common/credentials'; -import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; -import { Emitter } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; - -export class BrowserCredentialsService extends Disposable implements ICredentialsService { - - declare readonly _serviceBrand: undefined; - - private _onDidChangePassword = this._register(new Emitter()); - readonly onDidChangePassword = this._onDidChangePassword.event; - - private credentialsProvider: ICredentialsProvider; - - private _secretStoragePrefix: Promise; - public async getSecretStoragePrefix() { return this._secretStoragePrefix; } - - constructor( - @IBrowserWorkbenchEnvironmentService environmentService: IBrowserWorkbenchEnvironmentService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IProductService private readonly productService: IProductService - ) { - super(); - - if ( - environmentService.remoteAuthority - && !environmentService.options?.credentialsProvider - && !environmentService.options?.secretStorageProvider - ) { - // If we have a remote authority but the embedder didn't provide a credentialsProvider, - // we can use the CredentialsService on the remote side - const remoteCredentialsService = ProxyChannel.toService(remoteAgentService.getConnection()!.getChannel('credentials')); - this.credentialsProvider = remoteCredentialsService; - this._secretStoragePrefix = remoteCredentialsService.getSecretStoragePrefix(); - } else { - // fall back to InMemoryCredentialsProvider if none was given to us. - this.credentialsProvider = environmentService.options?.credentialsProvider ?? new InMemoryCredentialsProvider(); - this._secretStoragePrefix = Promise.resolve(this.productService.urlProtocol); - } - } - - getPassword(service: string, account: string): Promise { - return this.credentialsProvider.getPassword(service, account); - } - - async setPassword(service: string, account: string, password: string): Promise { - await this.credentialsProvider.setPassword(service, account, password); - - this._onDidChangePassword.fire({ service, account }); - } - - async deletePassword(service: string, account: string): Promise { - const didDelete = await this.credentialsProvider.deletePassword(service, account); - if (didDelete) { - this._onDidChangePassword.fire({ service, account }); - } - - return didDelete; - } - - findPassword(service: string): Promise { - return this.credentialsProvider.findPassword(service); - } - - findCredentials(service: string): Promise> { - return this.credentialsProvider.findCredentials(service); - } - - async clear(): Promise { - if (this.credentialsProvider.clear) { - return this.credentialsProvider.clear(); - } - } -} diff --git a/src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts b/src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts deleted file mode 100644 index a382e26301d..00000000000 --- a/src/vs/workbench/services/credentials/electron-sandbox/credentialsService.ts +++ /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. - *--------------------------------------------------------------------------------------------*/ - -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; -import { registerMainProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services'; - -registerMainProcessRemoteService(ICredentialsService, 'credentials'); diff --git a/src/vs/workbench/services/credentials/test/browser/credentialsService.test.ts b/src/vs/workbench/services/credentials/test/browser/credentialsService.test.ts deleted file mode 100644 index 8d1e35b4997..00000000000 --- a/src/vs/workbench/services/credentials/test/browser/credentialsService.test.ts +++ /dev/null @@ -1,61 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import { DisposableStore } from 'vs/base/common/lifecycle'; -import { BrowserCredentialsService } from 'vs/workbench/services/credentials/browser/credentialsService'; -import { TestEnvironmentService, TestRemoteAgentService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; - -suite('CredentialsService - web', () => { - const serviceId1 = 'test.credentialsService1'; - const serviceId2 = 'test.credentialsService2'; - const disposables = new DisposableStore(); - let credentialsService: BrowserCredentialsService; - setup(async () => { - credentialsService = disposables.add(new BrowserCredentialsService(TestEnvironmentService, new TestRemoteAgentService(), TestProductService)); - await credentialsService.setPassword(serviceId1, 'me1', '1'); - await credentialsService.setPassword(serviceId1, 'me2', '2'); - await credentialsService.setPassword(serviceId2, 'me3', '3'); - }); - - teardown(() => disposables.clear()); - - test('Gets correct values for service', async () => { - const credentials = await credentialsService.findCredentials(serviceId1); - assert.strictEqual(credentials.length, 2); - assert.strictEqual(credentials[0].password, '1'); - }); - - test('Gets correct value for credential', async () => { - const credentials = await credentialsService.getPassword(serviceId1, 'me1'); - assert.strictEqual(credentials, '1'); - }); - - test('Gets null for no account', async () => { - const credentials = await credentialsService.getPassword(serviceId1, 'doesnotexist'); - assert.strictEqual(credentials, null); - }); - - test('Gets null for no service or a different service', async () => { - let credentials = await credentialsService.getPassword('doesnotexist', 'me1'); - assert.strictEqual(credentials, null); - credentials = await credentialsService.getPassword(serviceId2, 'me1'); - assert.strictEqual(credentials, null); - }); - - test('Delete removes the value', async () => { - const result = await credentialsService.deletePassword(serviceId1, 'me1'); - assert.strictEqual(result, true); - const pass = await credentialsService.getPassword(serviceId1, 'me1'); - assert.strictEqual(pass, null); - }); - - test('Clear removes all values for service', async () => { - await credentialsService.clear(); - const credentials = await credentialsService.findCredentials(serviceId1); - assert.strictEqual(credentials.length, 0); - }); -}); diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts index ef77a1a411d..a19eae35e5f 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncInit.ts @@ -30,7 +30,6 @@ import { isEqual } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; 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'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; @@ -48,7 +47,6 @@ export class UserDataSyncInitializer implements IUserDataInitializer { constructor( @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, @ISecretStorageService private readonly secretStorageService: ISecretStorageService, - @ICredentialsService private readonly credentialsService: ICredentialsService, @IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService, @IFileService private readonly fileService: IFileService, @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @@ -92,7 +90,7 @@ export class UserDataSyncInitializer implements IUserDataInitializer { let authenticationSession; try { - authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); + authenticationSession = await getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService); } catch (error) { this.logService.error(error); } diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index d509a21c76c..4909ab5d1a4 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -30,7 +30,6 @@ import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UserDataSyncStoreClient } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncStoreTypeSynchronizer } from 'vs/platform/userDataSync/common/globalStateSync'; -import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { CancellationError } from 'vs/base/common/errors'; import { raceCancellationError } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -105,7 +104,6 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat @IProductService private readonly productService: IProductService, @IExtensionService private readonly extensionService: IExtensionService, @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, - @ICredentialsService private readonly credentialsService: ICredentialsService, @ISecretStorageService private readonly secretStorageService: ISecretStorageService, @INotificationService private readonly notificationService: INotificationService, @IProgressService private readonly progressService: IProgressService, @@ -172,7 +170,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat private async initialize(): Promise { if (isWeb) { - const authenticationSession = await getCurrentAuthenticationSessionInfo(this.credentialsService, this.secretStorageService, this.productService); + const authenticationSession = await getCurrentAuthenticationSessionInfo(this.secretStorageService, this.productService); if (this.currentSessionId === undefined && authenticationSession?.id) { if (this.environmentService.options?.settingsSyncOptions?.authenticationProvider && this.environmentService.options.settingsSyncOptions.enabled) { this.currentSessionId = authenticationSession.id; diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 1e429387e2d..01041abd0d5 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -56,7 +56,6 @@ import 'vs/workbench/services/path/electron-sandbox/pathService'; import 'vs/workbench/services/themes/electron-sandbox/nativeHostColorSchemeService'; import 'vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementService'; 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/secrets/electron-sandbox/secretStorageService'; import 'vs/workbench/services/localization/electron-sandbox/languagePackService'; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 8306e4eac0f..649a54cdb8c 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -43,7 +43,6 @@ import 'vs/workbench/services/extensionManagement/browser/webExtensionsScannerSe import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService'; import 'vs/workbench/services/extensionManagement/browser/extensionUrlTrustService'; import 'vs/workbench/services/telemetry/browser/telemetryService'; -import 'vs/workbench/services/credentials/browser/credentialsService'; import 'vs/workbench/services/url/browser/urlService'; import 'vs/workbench/services/update/browser/updateService'; import 'vs/workbench/services/workspaces/browser/workspacesService'; diff --git a/yarn.lock b/yarn.lock index 171f2e83601..59f8d5ce788 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1006,11 +1006,6 @@ resolved "https://registry.yarnpkg.com/@types/kerberos/-/kerberos-1.1.2.tgz#2a774abd48f727852f697d74241e9de3aea8e646" integrity sha512-cLixfcXjdj7qohLasmC1G4fh+en4e4g7mFZiG38D+K9rS9BRKFlq1JH5dGkQzICckbu4wM+RcwSa4VRHlBg7Rg== -"@types/keytar@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@types/keytar/-/keytar-4.4.0.tgz#ca24e6ee6d0df10c003aafe26e93113b8faf0d8e" - integrity sha512-cq/NkUUy6rpWD8n7PweNQQBpw2o0cf5v6fbkUVEpOB9VzzIvyPvSEId1/goIj+MciW2v1Lw5mRimKO01XgE9EA== - "@types/keyv@^3.1.4": version "3.1.4" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6" @@ -1826,11 +1821,6 @@ append-buffer@^1.0.2: dependencies: buffer-equal "^1.0.0" -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - arch@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" @@ -1846,14 +1836,6 @@ are-docs-informative@^0.0.2: resolved "https://registry.yarnpkg.com/are-docs-informative/-/are-docs-informative-0.0.2.tgz#387f0e93f5d45280373d387a59d34c96db321963" integrity sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig== -are-we-there-yet@~1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" - integrity sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -2847,11 +2829,6 @@ config-chain@^1.1.12: ini "^1.3.4" proto-list "~1.2.1" -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= - content-disposition@^0.5.4, content-disposition@~0.5.2: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -4651,20 +4628,6 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - integrity sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -5256,11 +5219,6 @@ has-symbols@^1.0.2: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -6238,14 +6196,6 @@ keygrip@~1.1.0: dependencies: tsscmp "1.0.6" -keytar@7.9.0: - version "7.9.0" - resolved "https://registry.yarnpkg.com/keytar/-/keytar-7.9.0.tgz#4c6225708f51b50cbf77c5aae81721964c2918cb" - integrity sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ== - dependencies: - node-addon-api "^4.3.0" - prebuild-install "^7.0.1" - keyv@^4.0.0: version "4.5.2" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" @@ -7265,16 +7215,6 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" -npmlog@^4.0.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -7294,7 +7234,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -object-assign@4.X, object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@4.X, object-assign@^4.0.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -8238,25 +8178,6 @@ prebuild-install@7.1.1: tar-fs "^2.0.0" tunnel-agent "^0.6.0" -prebuild-install@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.0.1.tgz#c10075727c318efe72412f333e0ef625beaf3870" - integrity sha512-QBSab31WqkyxpnMWQxubYAHR5S9B2+r81ucocew34Fkl98FhvKIF50jIJnNOBmAZfyNV7vE5T6gd3hTVWgY6tg== - dependencies: - detect-libc "^2.0.0" - expand-template "^2.0.3" - github-from-package "0.0.0" - minimist "^1.2.3" - mkdirp-classic "^0.5.3" - napi-build-utils "^1.0.1" - node-abi "^3.3.0" - npmlog "^4.0.1" - pump "^3.0.0" - rc "^1.2.7" - simple-get "^4.0.0" - tar-fs "^2.0.0" - tunnel-agent "^0.6.0" - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -8468,7 +8389,7 @@ read-pkg@^3.0.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -8928,7 +8849,7 @@ serialize-javascript@6.0.0, serialize-javascript@^6.0.0: dependencies: randombytes "^2.1.0" -set-blocking@^2.0.0, set-blocking@~2.0.0: +set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= @@ -9352,7 +9273,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.0: +string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -10644,13 +10565,6 @@ which@^1.2.14, which@^1.2.9: dependencies: isexe "^2.0.0" -wide-align@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" - integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== - dependencies: - string-width "^1.0.2 || 2" - wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" From 5bfedc46bc703ee0dc12526c88d0523cfcd2e63f Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Tue, 5 Sep 2023 17:48:18 -0700 Subject: [PATCH 533/607] Allow slash commands to return trees and followups (#192247) * Allow slash commands to return tree data * Allow slash commands to return followups * Revive URIs in tree data --- .../api/browser/mainThreadChatSlashCommands.ts | 5 +++-- .../api/common/extHostChatSlashCommand.ts | 14 ++++++++++++-- .../contrib/chat/common/chatServiceImpl.ts | 18 ++++++++++++++---- .../contrib/chat/common/chatSlashCommands.ts | 11 ++++++----- .../vscode.proposed.chatSlashCommands.d.ts | 6 +++--- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadChatSlashCommands.ts b/src/vs/workbench/api/browser/mainThreadChatSlashCommands.ts index a9ca433b4ba..b5955be9401 100644 --- a/src/vs/workbench/api/browser/mainThreadChatSlashCommands.ts +++ b/src/vs/workbench/api/browser/mainThreadChatSlashCommands.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableMap } from 'vs/base/common/lifecycle'; +import { revive } from 'vs/base/common/marshalling'; import { IProgress } from 'vs/platform/progress/common/progress'; import { ExtHostChatSlashCommandsShape, ExtHostContext, MainContext, MainThreadChatSlashCommandsShape } from 'vs/workbench/api/common/extHost.protocol'; import { IChatSlashCommandService, IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; @@ -42,7 +43,7 @@ export class MainThreadChatSlashCommands implements MainThreadChatSlashCommandsS const requestId = Math.random(); this._pendingProgress.set(requestId, progress); try { - await this._proxy.$executeCommand(handle, requestId, prompt, { history }, token); + return await this._proxy.$executeCommand(handle, requestId, prompt, { history }, token); } finally { this._pendingProgress.delete(requestId); } @@ -51,7 +52,7 @@ export class MainThreadChatSlashCommands implements MainThreadChatSlashCommandsS } async $handleProgressChunk(requestId: number, chunk: IChatSlashFragment): Promise { - this._pendingProgress.get(requestId)?.report(chunk); + this._pendingProgress.get(requestId)?.report(revive(chunk)); } $unregisterCommand(handle: number): void { diff --git a/src/vs/workbench/api/common/extHostChatSlashCommand.ts b/src/vs/workbench/api/common/extHostChatSlashCommand.ts index 2282d15c900..395c7d41ffc 100644 --- a/src/vs/workbench/api/common/extHostChatSlashCommand.ts +++ b/src/vs/workbench/api/common/extHostChatSlashCommand.ts @@ -67,16 +67,26 @@ export class ExtHostChatSlashCommands implements ExtHostChatSlashCommandsShape { { history: context.history.map(typeConvert.ChatMessage.to) }, new Progress(p => { throwIfDone(); - this._proxy.$handleProgressChunk(requestId, { content: p.message.value }); + this._proxy.$handleProgressChunk(requestId, { content: isInteractiveProgressFileTree(p.message) ? p.message : p.message.value }); }), token ); try { - await raceCancellation(Promise.resolve(task), token); + return await raceCancellation(Promise.resolve(task).then((v) => { + if (v && 'followUp' in v) { + const convertedFollowup = v?.followUp?.map(f => typeConvert.ChatFollowup.from(f)); + return { followUp: convertedFollowup }; + } + return undefined; + }), token); } finally { done = true; commandExecution.complete(); } } } + +function isInteractiveProgressFileTree(thing: unknown): thing is vscode.InteractiveProgressFileTree { + return !!thing && typeof thing === 'object' && 'treeData' in thing; +} diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 15977702756..d49fc8a3496 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -24,7 +24,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { ChatModel, ChatWelcomeMessageModel, IChatModel, ISerializableChatData, ISerializableChatsData, isCompleteInteractiveProgressTreeData } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; -import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatRequest, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChat, IChatCompleteResponse, IChatDetail, IChatDynamicRequest, IChatFollowup, IChatProgress, IChatProvider, IChatProviderInfo, IChatReplyFollowup, IChatRequest, IChatResponse, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ISlashCommand, InteractiveSessionCopyKind, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashCommandService, IChatSlashFragment } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -483,6 +483,7 @@ export class ChatService extends Disposable implements IChatService { } let rawResponse: IChatResponse | null | undefined; + let slashCommandFollowups: IChatFollowup[] | void = []; if ((typeof resolvedCommand === 'string' && typeof message === 'string' && this.chatSlashCommandService.hasCommand(resolvedCommand))) { // contributed slash commands @@ -497,7 +498,12 @@ export class ChatService extends Disposable implements IChatService { history.push({ role: ChatMessageRole.Assistant, content: request.response.response.value.value }); } } - await this.chatSlashCommandService.executeCommand(resolvedCommand, message.substring(resolvedCommand.length + 1).trimStart(), new Progress(p => progressCallback(p)), history, token); + const commandResult = await this.chatSlashCommandService.executeCommand(resolvedCommand, message.substring(resolvedCommand.length + 1).trimStart(), new Progress(p => { + const { content } = p; + const data = isCompleteInteractiveProgressTreeData(content) ? content : { content }; + progressCallback(data); + }), history, token); + slashCommandFollowups = commandResult?.followUp; rawResponse = { session: model.session! }; } else { @@ -541,10 +547,14 @@ export class ChatService extends Disposable implements IChatService { // TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593 if (provider.provideFollowups) { - Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { - model.setFollowups(request, followups ?? undefined); + Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(providerFollowups => { + const allFollowups = providerFollowups?.concat(slashCommandFollowups ?? []); + model.setFollowups(request, allFollowups ?? undefined); model.completeResponse(request); }); + } else if (slashCommandFollowups?.length) { + model.setFollowups(request, slashCommandFollowups); + model.completeResponse(request); } else { model.completeResponse(request); } diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index b64d5e6a5b8..2ba7cd8ada7 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -12,6 +12,7 @@ import { localize } from 'vs/nls'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress } from 'vs/platform/progress/common/progress'; import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; +import { IChatFollowup, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService'; import { IExtensionService, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; @@ -71,10 +72,10 @@ function isChatSlashData(data: any): data is IChatSlashData { } export interface IChatSlashFragment { - content: string; + content: string | { treeData: IChatResponseProgressFileTreeData }; } -export type IChatSlashCallback = { (prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise }; +export type IChatSlashCallback = { (prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> }; export const IChatSlashCommandService = createDecorator('chatSlashCommandService'); @@ -84,7 +85,7 @@ export interface IChatSlashCommandService { registerSlashData(data: IChatSlashData): IDisposable; registerSlashCallback(id: string, command: IChatSlashCallback): IDisposable; registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable; - executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise; + executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>; getCommands(): Array; hasCommand(id: string): boolean; } @@ -171,7 +172,7 @@ export class ChatSlashCommandService implements IChatSlashCommandService { return this._commands.has(id); } - async executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise { + async executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> { const data = this._commands.get(id); if (!data) { throw new Error('No command with id ${id} NOT registered'); @@ -183,6 +184,6 @@ export class ChatSlashCommandService implements IChatSlashCommandService { throw new Error(`No command with id ${id} NOT resolved`); } - await data.command(prompt, progress, history, token); + return await data.command(prompt, progress, history, token); } } diff --git a/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts b/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts index 5519c112ebe..7229fb15679 100644 --- a/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts +++ b/src/vscode-dts/vscode.proposed.chatSlashCommands.d.ts @@ -24,12 +24,12 @@ declare module 'vscode' { } export interface SlashResponse { - message: MarkdownString; + message: MarkdownString | InteractiveProgressFileTree; // edits?: TextEdit[] | WorkspaceEdit; } export interface SlashResult { - // followUp?: InteractiveSessionFollowup[]; + followUp?: InteractiveSessionFollowup[]; } export interface SlashCommandMetadata { @@ -38,7 +38,7 @@ declare module 'vscode' { export interface SlashCommand { - (prompt: ChatMessage, context: SlashCommandContext, progress: Progress, token: CancellationToken): Thenable; + (prompt: ChatMessage, context: SlashCommandContext, progress: Progress, token: CancellationToken): Thenable; } export namespace chat { From 58a4db6b5b547e0677e6cb7cc33ec28e245c3c09 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 5 Sep 2023 17:51:35 -0700 Subject: [PATCH 534/607] testing: fix markdown styling in test results view (#192251) Fixes #191292 --- .../workbench/contrib/testing/browser/media/testing.css | 8 ++++---- .../contrib/testing/browser/testingOutputPeek.css | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index 060e138c348..77b8a2fd3c1 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -210,20 +210,20 @@ height: 100%; } -.monaco-editor .zone-widget.test-output-peek .preview-text { +.test-output-peek-message-container .preview-text { padding: 8px 12px 8px 20px; height: calc(100% - 16px); } -.monaco-editor .zone-widget.test-output-peek .preview-text p:first-child { +.test-output-peek-message-container .preview-text p:first-child { margin-top: 0; } -.monaco-editor .zone-widget.test-output-peek .preview-text p:last-child { +.test-output-peek-message-container .preview-text p:last-child { margin-bottom: 0; } -.monaco-editor .zone-widget.test-output-peek .preview-text a { +.test-output-peek-message-container .preview-text a { cursor: pointer; } diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.css b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.css index 0daecae72f2..0d21a1260f1 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.css +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.css @@ -13,10 +13,10 @@ color: var(--vscode-peekViewResult-selectionForeground) !important; } -.monaco-editor .test-output-peek .test-output-peek-message-container a { +.test-output-peek-message-container a { color: var(--vscode-textLink-foreground); } -.monaco-editor .test-output-peek .test-output-peek-message-container a :hover { +.test-output-peek-message-container a :hover { color: var(--vscode-textLink-activeForeground); } From d3f6b37596d95482760419a6e01701b666af6be2 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 5 Sep 2023 18:04:07 -0700 Subject: [PATCH 535/607] Fix more leaks in chat service --- src/vs/base/test/common/utils.ts | 3 +- .../contrib/chat/common/chatServiceImpl.ts | 135 +++++++++--------- .../contrib/chat/common/chatSlashCommands.ts | 58 ++++---- .../chat/test/common/chatService.test.ts | 54 +++---- 4 files changed, 135 insertions(+), 115 deletions(-) diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index 280795cba38..188ca4f19bd 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -62,7 +62,8 @@ export class DisposableTracker implements IDisposableTracker { trackDisposable(d: IDisposable): void { const data = this.getDisposableData(d); if (!data.source) { - data.source = new Error().stack!; + data.source = + new Error().stack!; } } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 15977702756..a848f41fd0e 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -464,7 +464,7 @@ export class ChatService extends Disposable implements IChatService { }; const stopWatch = new StopWatch(false); - token.onCancellationRequested(() => { + const listener = token.onCancellationRequested(() => { this.trace('sendRequest', `Request for session ${model.sessionId} was cancelled`); this.telemetryService.publicLog2('interactiveSessionProviderInvoked', { providerId: provider.id, @@ -478,76 +478,81 @@ export class ChatService extends Disposable implements IChatService { model.cancelRequest(request); }); - if (usedSlashCommand?.command) { - this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); - } - let rawResponse: IChatResponse | null | undefined; + try { + if (usedSlashCommand?.command) { + this._onDidSubmitSlashCommand.fire({ slashCommand: usedSlashCommand.command, sessionId: model.sessionId }); + } - if ((typeof resolvedCommand === 'string' && typeof message === 'string' && this.chatSlashCommandService.hasCommand(resolvedCommand))) { - // contributed slash commands - // TODO: spell this out in the UI - const history: IChatMessage[] = []; - for (const request of model.getRequests()) { - if (typeof request.message !== 'string' || !request.response) { - continue; + let rawResponse: IChatResponse | null | undefined; + + if ((typeof resolvedCommand === 'string' && typeof message === 'string' && this.chatSlashCommandService.hasCommand(resolvedCommand))) { + // contributed slash commands + // TODO: spell this out in the UI + const history: IChatMessage[] = []; + for (const request of model.getRequests()) { + if (typeof request.message !== 'string' || !request.response) { + continue; + } + if (isMarkdownString(request.response.response.value)) { + history.push({ role: ChatMessageRole.User, content: request.message }); + history.push({ role: ChatMessageRole.Assistant, content: request.response.response.value.value }); + } } - if (isMarkdownString(request.response.response.value)) { - history.push({ role: ChatMessageRole.User, content: request.message }); - history.push({ role: ChatMessageRole.Assistant, content: request.response.response.value.value }); - } - } - await this.chatSlashCommandService.executeCommand(resolvedCommand, message.substring(resolvedCommand.length + 1).trimStart(), new Progress(p => progressCallback(p)), history, token); - rawResponse = { session: model.session! }; + await this.chatSlashCommandService.executeCommand(resolvedCommand, message.substring(resolvedCommand.length + 1).trimStart(), new Progress(p => progressCallback(p)), history, token); + rawResponse = { session: model.session! }; - } else { - const request: IChatRequest = { - session: model.session!, - message: resolvedCommand, - variables: {} - }; - - if (typeof request.message === 'string') { - const varResult = await this.chatVariablesService.resolveVariables(request.message, model, token); - request.variables = varResult.variables; - request.message = varResult.prompt; - } - - rawResponse = await provider.provideReply(request, progressCallback, token); - } - - if (token.isCancellationRequested) { - return; - } else { - if (!rawResponse) { - this.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`); - rawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', "Provider returned null response") } }; - } - - const result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' : - rawResponse.errorDetails && gotProgress ? 'errorWithOutput' : - rawResponse.errorDetails ? 'error' : - 'success'; - this.telemetryService.publicLog2('interactiveSessionProviderInvoked', { - providerId: provider.id, - timeToFirstProgress: rawResponse.timings?.firstProgress ?? 0, - totalTime: rawResponse.timings?.totalElapsed ?? 0, - result, - requestType, - slashCommand: usedSlashCommand?.command - }); - model.setResponse(request, rawResponse); - this.trace('sendRequest', `Provider returned response for session ${model.sessionId}`); - - // TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593 - if (provider.provideFollowups) { - Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { - model.setFollowups(request, followups ?? undefined); - model.completeResponse(request); - }); } else { - model.completeResponse(request); + const request: IChatRequest = { + session: model.session!, + message: resolvedCommand, + variables: {} + }; + + if (typeof request.message === 'string') { + const varResult = await this.chatVariablesService.resolveVariables(request.message, model, token); + request.variables = varResult.variables; + request.message = varResult.prompt; + } + + rawResponse = await provider.provideReply(request, progressCallback, token); } + + if (token.isCancellationRequested) { + return; + } else { + if (!rawResponse) { + this.trace('sendRequest', `Provider returned no response for session ${model.sessionId}`); + rawResponse = { session: model.session!, errorDetails: { message: localize('emptyResponse', "Provider returned null response") } }; + } + + const result = rawResponse.errorDetails?.responseIsFiltered ? 'filtered' : + rawResponse.errorDetails && gotProgress ? 'errorWithOutput' : + rawResponse.errorDetails ? 'error' : + 'success'; + this.telemetryService.publicLog2('interactiveSessionProviderInvoked', { + providerId: provider.id, + timeToFirstProgress: rawResponse.timings?.firstProgress ?? 0, + totalTime: rawResponse.timings?.totalElapsed ?? 0, + result, + requestType, + slashCommand: usedSlashCommand?.command + }); + model.setResponse(request, rawResponse); + this.trace('sendRequest', `Provider returned response for session ${model.sessionId}`); + + // TODO refactor this or rethink the API https://github.com/microsoft/vscode-copilot/issues/593 + if (provider.provideFollowups) { + Promise.resolve(provider.provideFollowups(model.session!, CancellationToken.None)).then(followups => { + model.setFollowups(request, followups ?? undefined); + model.completeResponse(request); + }); + } else { + model.completeResponse(request); + } + } + } finally { + listener.dispose(); } }); this._pendingRequests.set(model.sessionId, rawResponsePromise); diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index 5fbe93182bd..ea467d64935 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -11,9 +11,12 @@ import { Disposable, DisposableStore, IDisposable, combinedDisposable, toDisposa import { localize } from 'vs/nls'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProgress } from 'vs/platform/progress/common/progress'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IChatMessage } from 'vs/workbench/contrib/chat/common/chatProvider'; import { IExtensionService, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; //#region extension point @@ -102,30 +105,6 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom constructor(@IExtensionService private readonly _extensionService: IExtensionService) { super(); - const contributions = this._register(new DisposableStore()); - - slashesExtPoint.setHandler(extensions => { - contributions.clear(); - - for (const entry of extensions) { - if (!isProposedApiEnabled(entry.description, 'chatSlashCommands')) { - entry.collector.error(`The ${slashesExtPoint.name} is proposed API`); - continue; - } - - const { value } = entry; - - for (const candidate of Iterable.wrap(value)) { - - if (!isChatSlashData(candidate)) { - entry.collector.error(localize('invalid', "Invalid {0}: {1}", slashesExtPoint.name, JSON.stringify(candidate))); - continue; - } - - contributions.add(this.registerSlashData({ ...candidate })); - } - } - }); } override dispose(): void { @@ -186,3 +165,34 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom await data.command(prompt, progress, history, token); } } + +class ChatSlashCommandContribution implements IWorkbenchContribution { + constructor(@IChatSlashCommandService slashCommandService: IChatSlashCommandService) { + const contributions = new DisposableStore(); + + slashesExtPoint.setHandler(extensions => { + contributions.clear(); + + for (const entry of extensions) { + if (!isProposedApiEnabled(entry.description, 'chatSlashCommands')) { + entry.collector.error(`The ${slashesExtPoint.name} is proposed API`); + continue; + } + + const { value } = entry; + + for (const candidate of Iterable.wrap(value)) { + + if (!isChatSlashData(candidate)) { + entry.collector.error(localize('invalid', "Invalid {0}: {1}", slashesExtPoint.name, JSON.stringify(candidate))); + continue; + } + + contributions.add(slashCommandService.registerSlashData({ ...candidate })); + } + } + }); + } +} + +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ChatSlashCommandContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index c340299f47a..de602fdfa98 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -16,6 +16,8 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IViewsService } from 'vs/workbench/common/views'; import { IChatContributionService } from 'vs/workbench/contrib/chat/common/chatContributionService'; @@ -67,30 +69,32 @@ suite('Chat', () => { setup(async () => { instantiationService = testDisposables.add(new TestInstantiationService(new ServiceCollection( - [IChatSlashCommandService, new SyncDescriptor(ChatSlashCommandService)], + // [IChatSlashCommandService, new SyncDescriptor(ChatSlashCommandService)], [IChatVariablesService, new SyncDescriptor(ChatVariablesService)] ))); instantiationService.stub(IStorageService, storageService = testDisposables.add(new TestStorageService())); instantiationService.stub(ILogService, new NullLogService()); + instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IExtensionService, new TestExtensionService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IViewsService, new TestExtensionService()); instantiationService.stub(IChatContributionService, new TestExtensionService()); instantiationService.stub(IWorkspaceContextService, new TestContextService()); + instantiationService.stub(IChatSlashCommandService, testDisposables.add(instantiationService.createInstance(ChatSlashCommandService))); }); test('retrieveSession', async () => { const testService = testDisposables.add(instantiationService.createInstance(ChatService)); - const provider1 = new SimpleTestProvider('provider1'); - const provider2 = new SimpleTestProvider('provider2'); - testService.registerProvider(provider1); - testService.registerProvider(provider2); + const provider1 = testDisposables.add(new SimpleTestProvider('provider1')); + const provider2 = testDisposables.add(new SimpleTestProvider('provider2')); + testDisposables.add(testService.registerProvider(provider1)); + testDisposables.add(testService.registerProvider(provider2)); - const session1 = testService.startSession('provider1', CancellationToken.None); + const session1 = testDisposables.add(testService.startSession('provider1', CancellationToken.None)); await session1.waitForInitialization(); session1!.addRequest('request 1'); - const session2 = testService.startSession('provider2', CancellationToken.None); + const session2 = testDisposables.add(testService.startSession('provider2', CancellationToken.None)); await session2.waitForInitialization(); session2!.addRequest('request 2'); @@ -101,11 +105,11 @@ suite('Chat', () => { storageService.flush(); const testService2 = testDisposables.add(instantiationService.createInstance(ChatService)); - testService2.registerProvider(provider1); - testService2.registerProvider(provider2); - const retrieved1 = testService2.getOrRestoreSession(session1.sessionId); + testDisposables.add(testService2.registerProvider(provider1)); + testDisposables.add(testService2.registerProvider(provider2)); + const retrieved1 = testDisposables.add(testService2.getOrRestoreSession(session1.sessionId)!); await retrieved1!.waitForInitialization(); - const retrieved2 = testService2.getOrRestoreSession(session2.sessionId); + const retrieved2 = testDisposables.add(testService2.getOrRestoreSession(session2.sessionId)!); await retrieved2!.waitForInitialization(); assert.deepStrictEqual(provider1.lastInitialState, { state: 'provider1_state' }); assert.deepStrictEqual(provider2.lastInitialState, { state: 'provider2_state' }); @@ -131,16 +135,16 @@ suite('Chat', () => { const testService = testDisposables.add(instantiationService.createInstance(ChatService)); const provider1 = getFailProvider('provider1'); - testService.registerProvider(provider1); + testDisposables.add(testService.registerProvider(provider1)); - const session1 = testService.startSession('provider1', CancellationToken.None); + const session1 = testDisposables.add(testService.startSession('provider1', CancellationToken.None)); await assert.rejects(() => session1.waitForInitialization()); }); test('Can\'t register same provider id twice', async () => { const testService = testDisposables.add(instantiationService.createInstance(ChatService)); const id = 'testProvider'; - testService.registerProvider({ + testDisposables.add(testService.registerProvider({ id, displayName: 'Test', prepareSession: function (initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult { @@ -149,10 +153,10 @@ suite('Chat', () => { provideReply: function (request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult { throw new Error('Function not implemented.'); } - }); + })); assert.throws(() => { - testService.registerProvider({ + testDisposables.add(testService.registerProvider({ id, displayName: 'Test', prepareSession: function (initialState: IPersistedChatState | undefined, token: CancellationToken): ProviderResult { @@ -161,13 +165,13 @@ suite('Chat', () => { provideReply: function (request: IChatRequest, progress: (progress: IChatProgress) => void, token: CancellationToken): ProviderResult { throw new Error('Function not implemented.'); } - }); + })); }, 'Expected to throw for dupe provider'); }); test('getSlashCommands', async () => { const testService = testDisposables.add(instantiationService.createInstance(ChatService)); - const provider = new class extends SimpleTestProvider { + const provider = testDisposables.add(new class extends SimpleTestProvider { constructor() { super('testProvider'); } @@ -181,11 +185,11 @@ suite('Chat', () => { } ]; } - }; + }); - testService.registerProvider(provider); + testDisposables.add(testService.registerProvider(provider)); - const model = testService.startSession('testProvider', CancellationToken.None); + const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None)); const commands = await testService.getSlashCommands(model.sessionId, CancellationToken.None); assert.strictEqual(commands?.length, 1); @@ -196,9 +200,9 @@ suite('Chat', () => { test('sendRequestToProvider', async () => { const testService = testDisposables.add(instantiationService.createInstance(ChatService)); - testService.registerProvider(new SimpleTestProvider('testProvider')); + testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider')))); - const model = testService.startSession('testProvider', CancellationToken.None); + const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None)); assert.strictEqual(model.getRequests().length, 0); await testService.sendRequestToProvider(model.sessionId, { message: 'test request' }); @@ -207,9 +211,9 @@ suite('Chat', () => { test('addCompleteRequest', async () => { const testService = testDisposables.add(instantiationService.createInstance(ChatService)); - testService.registerProvider(new SimpleTestProvider('testProvider')); + testDisposables.add(testService.registerProvider(testDisposables.add(new SimpleTestProvider('testProvider')))); - const model = testService.startSession('testProvider', CancellationToken.None); + const model = testDisposables.add(testService.startSession('testProvider', CancellationToken.None)); assert.strictEqual(model.getRequests().length, 0); await testService.addCompleteRequest(model.sessionId, 'test request', { message: 'test response' }); From e00e7db6a4758e89271d85949a33697e34de6db0 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 5 Sep 2023 19:19:38 -0700 Subject: [PATCH 536/607] debug: fix launch task doesn't run if similar task is already running in different workspace folder (#192249) * debug: fix launch task doesn't run if similar task is already running in different workspace folder Fixes #191949 * more _id replacement --- .../contrib/debug/browser/debugTaskRunner.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts index 5026c9d18af..49904f73333 100644 --- a/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts +++ b/src/vs/workbench/contrib/debug/browser/debugTaskRunner.ts @@ -200,31 +200,32 @@ export class DebugTaskRunner { // If a task is missing the problem matcher the promise will never complete, so we need to have a workaround #35340 let taskStarted = false; - const inactivePromise: Promise = new Promise((c, e) => once(e => { + const taskKey = task.getMapKey(); + const inactivePromise: Promise = new Promise((c) => once(e => { // When a task isBackground it will go inactive when it is safe to launch. // But when a background task is terminated by the user, it will also fire an inactive event. // This means that we will not get to see the real exit code from running the task (undefined when terminated by the user). // Catch the ProcessEnded event here, which occurs before inactive, and capture the exit code to prevent this. return (e.kind === TaskEventKind.Inactive || (e.kind === TaskEventKind.ProcessEnded && e.exitCode === undefined)) - && e.taskId === task._id; + && e.__task?.getMapKey() === taskKey; }, this.taskService.onDidStateChange)(e => { taskStarted = true; c(e.kind === TaskEventKind.ProcessEnded ? { exitCode: e.exitCode } : null); })); const promise: Promise = this.taskService.getActiveTasks().then(async (tasks): Promise => { - if (tasks.find(t => t._id === task._id)) { + if (tasks.find(t => t.getMapKey() === taskKey)) { // Check that the task isn't busy and if it is, wait for it const busyTasks = await this.taskService.getBusyTasks(); - if (busyTasks.find(t => t._id === task._id)) { + if (busyTasks.find(t => t.getMapKey() === taskKey)) { taskStarted = true; return inactivePromise; } // task is already running and isn't busy - nothing to do. return Promise.resolve(null); } - once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.taskId === task._id, this.taskService.onDidStateChange)(() => { + once(e => ((e.kind === TaskEventKind.Active) || (e.kind === TaskEventKind.DependsOnStarted)) && e.__task?.getMapKey() === taskKey, this.taskService.onDidStateChange)(() => { // Task is active, so everything seems to be fine, no need to prompt after 10 seconds // Use case being a slow running task should not be prompted even though it takes more than 10 seconds taskStarted = true; @@ -238,7 +239,7 @@ export class DebugTaskRunner { }); return new Promise((c, e) => { - const waitForInput = new Promise(resolve => once(e => (e.kind === TaskEventKind.AcquiredInput) && e.taskId === task._id, this.taskService.onDidStateChange)(() => { + const waitForInput = new Promise(resolve => once(e => (e.kind === TaskEventKind.AcquiredInput) && e.__task?.getMapKey() === taskKey, this.taskService.onDidStateChange)(() => { resolve(); })); From 225ab90d8329f29264dd98330651d33b8cd6b1e0 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 5 Sep 2023 23:52:49 -0700 Subject: [PATCH 537/607] Have secrets adopt ensureNoDisposablesAreLeakedInTestSuite (#192258) ref https://github.com/microsoft/vscode/issues/190503 --- src/vs/platform/secrets/common/secrets.ts | 14 ++++---- .../secrets/test/common/secrets.test.ts | 32 +++++++++++++++---- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/secrets/common/secrets.ts b/src/vs/platform/secrets/common/secrets.ts index 87d2653f6ef..7bba894a909 100644 --- a/src/vs/platform/secrets/common/secrets.ts +++ b/src/vs/platform/secrets/common/secrets.ts @@ -9,7 +9,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IStorageService, InMemoryStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { Emitter, Event } from 'vs/base/common/event'; import { ILogService } from 'vs/platform/log/common/log'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Lazy } from 'vs/base/common/lazy'; export const ISecretStorageService = createDecorator('secretStorageService'); @@ -26,26 +26,28 @@ export interface ISecretStorageService extends ISecretStorageProvider { onDidChangeSecret: Event; } -export class BaseSecretStorageService implements ISecretStorageService { +export class BaseSecretStorageService extends Disposable implements ISecretStorageService { declare readonly _serviceBrand: undefined; private readonly _storagePrefix = 'secret://'; - protected readonly onDidChangeSecretEmitter = new Emitter(); + protected readonly onDidChangeSecretEmitter = this._register(new Emitter()); onDidChangeSecret: Event = this.onDidChangeSecretEmitter.event; protected readonly _sequencer = new SequencerByKey(); private _type: 'in-memory' | 'persisted' | 'unknown' = 'unknown'; - private readonly _onDidChangeValueDisposable = new DisposableStore(); + private readonly _onDidChangeValueDisposable = this._register(new DisposableStore()); constructor( private readonly _useInMemoryStorage: boolean, @IStorageService private _storageService: IStorageService, @IEncryptionService protected _encryptionService: IEncryptionService, @ILogService protected readonly _logService: ILogService, - ) { } + ) { + super(); + } /** * @Note initialize must be called first so that this can be resolved properly @@ -134,7 +136,7 @@ export class BaseSecretStorageService implements ISecretStorageService { } this._logService.trace('[SecretStorageService] Encryption is not available, falling back to in-memory storage'); this._type = 'in-memory'; - storageService = new InMemoryStorageService(); + storageService = this._register(new InMemoryStorageService()); } this._onDidChangeValueDisposable.clear(); diff --git a/src/vs/platform/secrets/test/common/secrets.test.ts b/src/vs/platform/secrets/test/common/secrets.test.ts index c22701d7cd6..b3a048af2f2 100644 --- a/src/vs/platform/secrets/test/common/secrets.test.ts +++ b/src/vs/platform/secrets/test/common/secrets.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IEncryptionService, KnownStorageProvider } from 'vs/platform/encryption/common/encryptionService'; import { NullLogService } from 'vs/platform/log/common/log'; import { BaseSecretStorageService } from 'vs/platform/secrets/common/secrets'; @@ -50,6 +51,8 @@ class TestNoEncryptionService implements IEncryptionService { } suite('secrets', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + suite('BaseSecretStorageService useInMemoryStorage=true', () => { let service: BaseSecretStorageService; let spyEncryptionService: sinon.SinonSpiedInstance; @@ -58,7 +61,12 @@ suite('secrets', () => { setup(() => { sandbox = sinon.createSandbox(); spyEncryptionService = sandbox.spy(new TestEncryptionService()); - service = new BaseSecretStorageService(true, new InMemoryStorageService(), spyEncryptionService, new NullLogService()); + service = store.add(new BaseSecretStorageService( + true, + store.add(new InMemoryStorageService()), + spyEncryptionService, + store.add(new NullLogService()) + )); }); teardown(() => { @@ -98,10 +106,10 @@ suite('secrets', () => { const key = 'my-secret'; const value = 'my-secret-value'; let eventFired = false; - service.onDidChangeSecret((changedKey) => { + store.add(service.onDidChangeSecret((changedKey) => { assert.strictEqual(changedKey, key); eventFired = true; - }); + })); await service.set(key, value); assert.strictEqual(eventFired, true); }); @@ -115,7 +123,12 @@ suite('secrets', () => { setup(() => { sandbox = sinon.createSandbox(); spyEncryptionService = sandbox.spy(new TestEncryptionService()); - service = new BaseSecretStorageService(false, new InMemoryStorageService(), spyEncryptionService, new NullLogService()); + service = store.add(new BaseSecretStorageService( + false, + store.add(new InMemoryStorageService()), + spyEncryptionService, + store.add(new NullLogService())) + ); }); teardown(() => { @@ -155,10 +168,10 @@ suite('secrets', () => { const key = 'my-secret'; const value = 'my-secret-value'; let eventFired = false; - service.onDidChangeSecret((changedKey) => { + store.add(service.onDidChangeSecret((changedKey) => { assert.strictEqual(changedKey, key); eventFired = true; - }); + })); await service.set(key, value); assert.strictEqual(eventFired, true); }); @@ -172,7 +185,12 @@ suite('secrets', () => { setup(() => { sandbox = sinon.createSandbox(); spyNoEncryptionService = sandbox.spy(new TestNoEncryptionService()); - service = new BaseSecretStorageService(false, new InMemoryStorageService(), spyNoEncryptionService, new NullLogService()); + service = store.add(new BaseSecretStorageService( + false, + store.add(new InMemoryStorageService()), + spyNoEncryptionService, + store.add(new NullLogService())) + ); }); teardown(() => { From 384e2636e5cf2e845f3860cbdb061518d2afbade Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 5 Sep 2023 23:53:15 -0700 Subject: [PATCH 538/607] Allow for querying multiple kinds of related information (#192259) --- .../common/aiRelatedInformationService.ts | 47 ++++++++----------- .../aiRelatedInformationService.test.ts | 32 +++++++++++-- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts index 7168c3b4eed..cd4e247b59e 100644 --- a/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts +++ b/src/vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from 'vs/base/common/cancellation'; -import { CancelablePromise, createCancelablePromise, raceCancellablePromises, timeout } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, raceTimeout } from 'vs/base/common/async'; import { IDisposable } from 'vs/base/common/lifecycle'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { StopWatch } from 'vs/base/common/stopwatch'; @@ -64,16 +64,8 @@ export class AiRelatedInformationService implements IAiRelatedInformationService const stopwatch = StopWatch.create(); - const cancellablePromises: Array> = []; - - const timer = timeout(AiRelatedInformationService.DEFAULT_TIMEOUT); - const disposable = token.onCancellationRequested(() => { - disposable.dispose(); - timer.cancel(); - }); - - for (const provider of providers) { - cancellablePromises.push(createCancelablePromise(async t => { + const cancellablePromises: Array> = providers.map((provider) => { + return createCancelablePromise(async t => { try { const result = await provider.provideAiRelatedInformation(query, t); // double filter just in case @@ -81,25 +73,26 @@ export class AiRelatedInformationService implements IAiRelatedInformationService } catch (e) { // logged in extension host } - // Wait for the timer to finish to allow for another provider to resolve. - // Alternatively, if something resolved, or we've timed out, this will throw - // as expected. - await timer; - throw new Error('Related information provider timed out'); - })); - } - - cancellablePromises.push(createCancelablePromise(async (t) => { - const disposable = t.onCancellationRequested(() => { - timer.cancel(); - disposable.dispose(); + return []; }); - await timer; - throw new Error('Related information provider timed out'); - })); + }); try { - const result = await raceCancellablePromises(cancellablePromises); + const results = await raceTimeout( + Promise.allSettled(cancellablePromises), + AiRelatedInformationService.DEFAULT_TIMEOUT, + () => { + cancellablePromises.forEach(p => p.cancel()); + throw new Error('Related information provider timed out'); + } + ); + if (!results) { + return []; + } + const result = results + .filter(r => r.status === 'fulfilled') + .map(r => (r as PromiseFulfilledResult).value) + .flat(); return result; } finally { stopwatch.stop(); diff --git a/src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts b/src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts index 6d521ca8777..62f25baed57 100644 --- a/src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts +++ b/src/vs/workbench/services/aiRelatedInformation/test/common/aiRelatedInformationService.test.ts @@ -6,19 +6,21 @@ import * as assert from 'assert'; import { AiRelatedInformationService } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformationService'; import { NullLogService } from 'vs/platform/log/common/log'; -import { CommandInformationResult, IAiRelatedInformationProvider, RelatedInformationType } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; +import { CommandInformationResult, IAiRelatedInformationProvider, RelatedInformationType, SettingInformationResult } from 'vs/workbench/services/aiRelatedInformation/common/aiRelatedInformation'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('AiRelatedInformationService', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); let service: AiRelatedInformationService; setup(() => { - service = new AiRelatedInformationService(new NullLogService()); + service = new AiRelatedInformationService(store.add(new NullLogService())); }); test('should check if providers are registered', () => { assert.equal(service.isEnabled(), false); - service.registerAiRelatedInformationProvider(RelatedInformationType.CommandInformation, { provideAiRelatedInformation: () => Promise.resolve([]) }); + store.add(service.registerAiRelatedInformationProvider(RelatedInformationType.CommandInformation, { provideAiRelatedInformation: () => Promise.resolve([]) })); assert.equal(service.isEnabled(), true); }); @@ -40,4 +42,28 @@ suite('AiRelatedInformationService', () => { assert.strictEqual(result.length, 1); assert.strictEqual((result[0] as CommandInformationResult).command, command); }); + + test('should get different types of related information', async () => { + const command = 'command'; + const commandProvider: IAiRelatedInformationProvider = { + provideAiRelatedInformation: () => Promise.resolve([{ type: RelatedInformationType.CommandInformation, command, weight: 1 }]) + }; + service.registerAiRelatedInformationProvider(RelatedInformationType.CommandInformation, commandProvider); + const setting = 'setting'; + const settingProvider: IAiRelatedInformationProvider = { + provideAiRelatedInformation: () => Promise.resolve([{ type: RelatedInformationType.SettingInformation, setting, weight: 1 }]) + }; + service.registerAiRelatedInformationProvider(RelatedInformationType.SettingInformation, settingProvider); + const result = await service.getRelatedInformation( + 'query', + [ + RelatedInformationType.CommandInformation, + RelatedInformationType.SettingInformation + ], + CancellationToken.None + ); + assert.strictEqual(result.length, 2); + assert.strictEqual((result[0] as CommandInformationResult).command, command); + assert.strictEqual((result[1] as SettingInformationResult).setting, setting); + }); }); From 3e6068253b511db7166a60c966022aa6fc1cd294 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 6 Sep 2023 08:54:08 +0200 Subject: [PATCH 539/607] storage - first cut action to see and remove large entries (#192013) * storage - first cut action to see and remove large entries * limit to dev only * update * support in web too * optimize db as well * add integration test * include large string * actually check on DB size --- src/vs/base/parts/storage/common/storage.ts | 17 +++ src/vs/base/parts/storage/node/storage.ts | 10 +- .../test/node/storage.integrationTest.ts | 140 ++++++++++++------ .../find/test/browser/findController.test.ts | 61 ++------ .../test/browser/multicursor.test.ts | 26 +--- src/vs/platform/storage/common/storage.ts | 14 ++ src/vs/platform/storage/common/storageIpc.ts | 6 + .../storage/electron-main/storageIpc.ts | 4 + .../storage/electron-main/storageMain.ts | 9 ++ .../browser/actions/developerActions.ts | 112 +++++++++++++- .../browser/parts/editor/editorPart.ts | 4 - .../storage/browser/storageService.ts | 4 + 12 files changed, 283 insertions(+), 124 deletions(-) diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index 71bbf4f9741..304da70de7d 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -49,6 +49,8 @@ export interface IStorageDatabase { getItems(): Promise>; updateItems(request: IUpdateRequest): Promise; + optimize(): Promise; + close(recovery?: () => Map): Promise; } @@ -99,6 +101,8 @@ export interface IStorage extends IDisposable { flush(delay?: number): Promise; whenFlushed(): Promise; + optimize(): Promise; + close(): Promise; } @@ -312,6 +316,18 @@ export class Storage extends Disposable implements IStorage { return this.doFlush(); } + async optimize(): Promise { + if (this.state === StorageState.Closed) { + return; // Return early if we are already closed + } + + // Await pending data to be flushed to the DB + // before attempting to optimize the DB + await this.flush(0); + + return this.database.optimize(); + } + async close(): Promise { if (!this.pendingClose) { this.pendingClose = this.doClose(); @@ -414,5 +430,6 @@ export class InMemoryStorageDatabase implements IStorageDatabase { request.delete?.forEach(key => this.items.delete(key)); } + async optimize(): Promise { } async close(): Promise { } } diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index e629ab01f0e..5d942007580 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -144,6 +144,14 @@ export class SQLiteStorageDatabase implements IStorageDatabase { }); } + async optimize(): Promise { + this.logger.trace(`[storage ${this.name}] vacuum()`); + + const connection = await this.whenConnected; + + return this.exec(connection, 'VACUUM'); + } + async close(recovery?: () => Map): Promise { this.logger.trace(`[storage ${this.name}] close()`); @@ -420,7 +428,7 @@ class SQLiteStorageDatabaseLogger { // to reduce lots of output, require an environment variable to enable tracing // this helps when running with --verbose normally where the storage tracing // might hide useful output to look at - static readonly VSCODE_TRACE_STORAGE = 'VSCODE_TRACE_STORAGE'; + private static readonly VSCODE_TRACE_STORAGE = 'VSCODE_TRACE_STORAGE'; private readonly logTrace: ((msg: string) => void) | undefined; private readonly logError: ((error: string | Error) => void) | undefined; diff --git a/src/vs/base/parts/storage/test/node/storage.integrationTest.ts b/src/vs/base/parts/storage/test/node/storage.integrationTest.ts index b0290ec7c60..51c66ca1af8 100644 --- a/src/vs/base/parts/storage/test/node/storage.integrationTest.ts +++ b/src/vs/base/parts/storage/test/node/storage.integrationTest.ts @@ -650,45 +650,31 @@ flakySuite('SQLite Storage Library', function () { test('very large item value', async function () { const storage = new SQLiteStorageDatabase(join(testdir, 'storage.db')); - const items = new Map(); - items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#098658"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#098658"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#098658"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#098658"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#098658"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); - items.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); + let randomData = createLargeRandomData(); // 3.6MB - let uuid = generateUuid(); - let value: string[] = []; - for (let i = 0; i < 100000; i++) { - value.push(uuid); - } - items.set('super.large.string', value.join()); // 3.6MB - - await storage.updateItems({ insert: items }); + await storage.updateItems({ insert: randomData.items }); let storedItems = await storage.getItems(); - strictEqual(items.get('colorthemedata'), storedItems.get('colorthemedata')); - strictEqual(items.get('commandpalette.mru.cache'), storedItems.get('commandpalette.mru.cache')); - strictEqual(items.get('super.large.string'), storedItems.get('super.large.string')); + strictEqual(randomData.items.get('colorthemedata'), storedItems.get('colorthemedata')); + strictEqual(randomData.items.get('commandpalette.mru.cache'), storedItems.get('commandpalette.mru.cache')); + strictEqual(randomData.items.get('super.large.string'), storedItems.get('super.large.string')); - uuid = generateUuid(); - value = []; - for (let i = 0; i < 100000; i++) { - value.push(uuid); - } - items.set('super.large.string', value.join()); // 3.6MB + randomData = createLargeRandomData(); - await storage.updateItems({ insert: items }); + await storage.updateItems({ insert: randomData.items }); storedItems = await storage.getItems(); - strictEqual(items.get('colorthemedata'), storedItems.get('colorthemedata')); - strictEqual(items.get('commandpalette.mru.cache'), storedItems.get('commandpalette.mru.cache')); - strictEqual(items.get('super.large.string'), storedItems.get('super.large.string')); + strictEqual(randomData.items.get('colorthemedata'), storedItems.get('colorthemedata')); + strictEqual(randomData.items.get('commandpalette.mru.cache'), storedItems.get('commandpalette.mru.cache')); + strictEqual(randomData.items.get('super.large.string'), storedItems.get('super.large.string')); const toDelete = new Set(); toDelete.add('super.large.string'); await storage.updateItems({ delete: toDelete }); storedItems = await storage.getItems(); - strictEqual(items.get('colorthemedata'), storedItems.get('colorthemedata')); - strictEqual(items.get('commandpalette.mru.cache'), storedItems.get('commandpalette.mru.cache')); + strictEqual(randomData.items.get('colorthemedata'), storedItems.get('colorthemedata')); + strictEqual(randomData.items.get('commandpalette.mru.cache'), storedItems.get('commandpalette.mru.cache')); ok(!storedItems.get('super.large.string')); await storage.close(); @@ -751,15 +737,7 @@ flakySuite('SQLite Storage Library', function () { test('lots of INSERT & DELETE (below inline max)', async () => { const storage = new SQLiteStorageDatabase(join(testdir, 'storage.db')); - const items = new Map(); - const keys: Set = new Set(); - for (let i = 0; i < 200; i++) { - const uuid = generateUuid(); - const key = `key: ${uuid}`; - - items.set(key, `value: ${uuid}`); - keys.add(key); - } + const { items, keys } = createManyRandomData(200); await storage.updateItems({ insert: items }); @@ -777,15 +755,7 @@ flakySuite('SQLite Storage Library', function () { test('lots of INSERT & DELETE (above inline max)', async () => { const storage = new SQLiteStorageDatabase(join(testdir, 'storage.db')); - const items = new Map(); - const keys: Set = new Set(); - for (let i = 0; i < 400; i++) { - const uuid = generateUuid(); - const key = `key: ${uuid}`; - - items.set(key, `value: ${uuid}`); - keys.add(key); - } + const { items, keys } = createManyRandomData(); await storage.updateItems({ insert: items }); @@ -813,4 +783,86 @@ flakySuite('SQLite Storage Library', function () { ok(error); }); + + test('optimize', async () => { + const dbPath = join(testdir, 'storage.db'); + let storage = new SQLiteStorageDatabase(dbPath); + + const { items, keys } = createManyRandomData(400, true); + + await storage.updateItems({ insert: items }); + + let storedItems = await storage.getItems(); + strictEqual(storedItems.size, items.size); + + await storage.optimize(); + await storage.close(); + + const sizeBeforeDeleteAndOptimize = (await Promises.stat(dbPath)).size; + + storage = new SQLiteStorageDatabase(dbPath); + + storedItems = await storage.getItems(); + strictEqual(storedItems.size, items.size); + + await storage.updateItems({ delete: keys }); + + storedItems = await storage.getItems(); + strictEqual(storedItems.size, 0); + + await storage.optimize(); + await storage.close(); + + storage = new SQLiteStorageDatabase(dbPath); + + storedItems = await storage.getItems(); + strictEqual(storedItems.size, 0); + + await storage.close(); + + const sizeAfterDeleteAndOptimize = (await Promises.stat(dbPath)).size; + + strictEqual(sizeAfterDeleteAndOptimize < sizeBeforeDeleteAndOptimize, true); + }); + + function createManyRandomData(length = 400, includeVeryLarge = false) { + const items = new Map(); + const keys = new Set(); + + for (let i = 0; i < length; i++) { + const uuid = generateUuid(); + const key = `key: ${uuid}`; + + items.set(key, `value: ${uuid}`); + keys.add(key); + } + + if (includeVeryLarge) { + const largeData = createLargeRandomData(); + for (const [key, value] of largeData.items) { + items.set(key, value); + keys.add(key); + } + } + + return { items, keys }; + } + + function createLargeRandomData() { + const items = new Map(); + items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#098658"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#098658"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#098658"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#098658"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#098658"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); + items.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); + + const uuid = generateUuid(); + const value: string[] = []; + for (let i = 0; i < 100000; i++) { + value.push(uuid); + } + + items.set('super.large.string', value.join()); // 3.6MB + + return { items, uuid, value }; + } }); + + diff --git a/src/vs/editor/contrib/find/test/browser/findController.test.ts b/src/vs/editor/contrib/find/test/browser/findController.test.ts index 86f44277667..9db7f06f6ce 100644 --- a/src/vs/editor/contrib/find/test/browser/findController.test.ts +++ b/src/vs/editor/contrib/find/test/browser/findController.test.ts @@ -5,7 +5,6 @@ import * as assert from 'assert'; import { Delayer } from 'vs/base/common/async'; -import { Event } from 'vs/base/common/event'; import * as platform from 'vs/base/common/platform'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction } from 'vs/editor/browser/editorExtensions'; @@ -20,7 +19,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, InMemoryStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; class TestFindController extends CommonFindController { @@ -64,28 +63,9 @@ function executeAction(instantiationService: IInstantiationService, editor: ICod } suite('FindController', () => { - const queryState: { [key: string]: any } = {}; let clipboardState = ''; const serviceCollection = new ServiceCollection(); - serviceCollection.set(IStorageService, { - _serviceBrand: undefined, - onDidChangeTarget: Event.None, - onDidChangeValue: () => Event.None, - onWillSaveState: Event.None, - get: (key: string) => queryState[key], - getBoolean: (key: string) => !!queryState[key], - getNumber: (key: string) => undefined!, - getObject: (key: string) => undefined!, - store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - storeAll: () => { throw new Error(); }, - remove: () => undefined, - isNew: () => false, - flush: () => { return Promise.resolve(); }, - keys: () => [], - log: () => { }, - switch: () => { throw new Error(); }, - hasScope() { return false; } - } as IStorageService); + serviceCollection.set(IStorageService, new InMemoryStorageService()); if (platform.isMacintosh) { serviceCollection.set(IClipboardService, { @@ -496,30 +476,12 @@ suite('FindController', () => { }); suite('FindController query options persistence', () => { - let queryState: { [key: string]: any } = {}; - queryState['editor.isRegex'] = false; - queryState['editor.matchCase'] = false; - queryState['editor.wholeWord'] = false; const serviceCollection = new ServiceCollection(); - serviceCollection.set(IStorageService, { - _serviceBrand: undefined, - onDidChangeTarget: Event.None, - onDidChangeValue: () => Event.None, - onWillSaveState: Event.None, - get: (key: string) => queryState[key], - getBoolean: (key: string) => !!queryState[key], - getNumber: (key: string) => undefined!, - getObject: (key: string) => undefined!, - store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - storeAll: () => { throw new Error(); }, - remove: () => undefined, - isNew: () => false, - flush: () => { return Promise.resolve(); }, - keys: () => [], - log: () => { }, - switch: () => { throw new Error(); }, - hasScope() { return false; } - } as IStorageService); + const storageService = new InMemoryStorageService(); + storageService.store('editor.isRegex', false, StorageScope.WORKSPACE, StorageTarget.USER); + storageService.store('editor.matchCase', false, StorageScope.WORKSPACE, StorageTarget.USER); + storageService.store('editor.wholeWord', false, StorageScope.WORKSPACE, StorageTarget.USER); + serviceCollection.set(IStorageService, storageService); test('matchCase', async () => { await withAsyncTestCodeEditor([ @@ -528,7 +490,7 @@ suite('FindController query options persistence', () => { 'XYZ', 'ABC' ], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => { - queryState = { 'editor.isRegex': false, 'editor.matchCase': true, 'editor.wholeWord': false }; + storageService.store('editor.matchCase', true, StorageScope.WORKSPACE, StorageTarget.USER); // The cursor is at the very top, of the file, at the first ABC const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); const findState = findController.getState(); @@ -545,7 +507,8 @@ suite('FindController query options persistence', () => { }); }); - queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; + storageService.store('editor.matchCase', false, StorageScope.WORKSPACE, StorageTarget.USER); + storageService.store('editor.wholeWord', true, StorageScope.WORKSPACE, StorageTarget.USER); test('wholeWord', async () => { await withAsyncTestCodeEditor([ @@ -554,7 +517,6 @@ suite('FindController query options persistence', () => { 'XYZ', 'ABC' ], { serviceCollection: serviceCollection }, async (editor, _, instantiationService) => { - queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; // The cursor is at the very top, of the file, at the first ABC const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); const findState = findController.getState(); @@ -578,11 +540,10 @@ suite('FindController query options persistence', () => { 'XYZ', 'ABC' ], { serviceCollection: serviceCollection }, async (editor) => { - queryState = { 'editor.isRegex': false, 'editor.matchCase': false, 'editor.wholeWord': true }; // The cursor is at the very top, of the file, at the first ABC const findController = editor.registerAndInstantiateContribution(TestFindController.ID, TestFindController); findController.toggleRegex(); - assert.strictEqual(queryState['editor.isRegex'], true); + assert.strictEqual(storageService.getBoolean('editor.isRegex', StorageScope.WORKSPACE), true); findController.dispose(); }); diff --git a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts index e19fc94e97c..d206905e4c3 100644 --- a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Event } from 'vs/base/common/event'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { Handler } from 'vs/editor/common/editorCommon'; @@ -12,11 +11,10 @@ import { CommonFindController } from 'vs/editor/contrib/find/browser/findControl import { AddSelectionToNextFindMatchAction, InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction } from 'vs/editor/contrib/multicursor/browser/multicursor'; import { ITestCodeEditor, withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; suite('Multicursor', () => { - test('issue #26393: Multiple cursors + Word wrap', () => { withTestCodeEditor([ 'a'.repeat(20), @@ -82,28 +80,8 @@ function fromRange(rng: Range): number[] { } suite('Multicursor selection', () => { - const queryState: { [key: string]: any } = {}; const serviceCollection = new ServiceCollection(); - serviceCollection.set(IStorageService, { - _serviceBrand: undefined, - onDidChangeValue: () => { throw new Error(); }, - onDidChangeValue2: Event.None, - onDidChangeTarget: Event.None, - onWillSaveState: Event.None, - get: (key: string) => queryState[key], - getBoolean: (key: string) => !!queryState[key], - getNumber: (key: string) => undefined!, - getObject: (key: string) => undefined!, - store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, - storeAll: () => { throw new Error(); }, - remove: (key) => undefined, - log: () => undefined, - switch: () => Promise.resolve(undefined), - flush: () => Promise.resolve(undefined), - isNew: () => true, - keys: () => [], - hasScope() { return false; } - } as IStorageService); + serviceCollection.set(IStorageService, new InMemoryStorageService()); test('issue #8817: Cursor position changes when you cancel multicursor', () => { withTestCodeEditor([ diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index fd8bd72be27..2af9ffd0281 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -204,6 +204,11 @@ export interface IStorageService { */ isNew(scope: StorageScope): boolean; + /** + * Attempts to reduce the DB size via optimization commands if supported. + */ + optimize(scope: StorageScope): Promise; + /** * Allows to flush state, e.g. in cases where a shutdown is * imminent. This will send out the `onWillSaveState` to ask @@ -625,6 +630,15 @@ export abstract class AbstractStorageService extends Disposable implements IStor ); } + async optimize(scope: StorageScope): Promise { + + // Await pending data to be flushed to the DB + // before attempting to optimize the DB + await this.flush(); + + return this.getStorage(scope)?.optimize(); + } + async switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise { // Signal as event so that clients can store data before we switch diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 07d2ecd4576..7abbec236c6 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -79,6 +79,12 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD return this.channel.call('updateItems', serializableRequest); } + optimize(): Promise { + const serializableRequest: IBaseSerializableStorageRequest = { profile: this.profile, workspace: this.workspace }; + + return this.channel.call('optimize', serializableRequest); + } + abstract close(): Promise; } diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index b645cf318b7..95c1d083aca 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -125,6 +125,10 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel break; } + case 'optimize': { + return storage.optimize(); + } + case 'isUsed': { const path = arg.payload as string | undefined; if (typeof path === 'string') { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index b993f4cb3cd..6843793692f 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -97,6 +97,11 @@ export interface IStorageMain extends IDisposable { */ isInMemory(): boolean; + /** + * Attempts to reduce the DB size via optimization commands if supported. + */ + optimize(): Promise; + /** * Close the storage connection. */ @@ -216,6 +221,10 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { return this._storage.delete(key); } + optimize(): Promise { + return this._storage.optimize(); + } + async close(): Promise { // Measure how long it takes to close storage diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 48c3f08a46e..e226162d5f5 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -20,7 +20,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { Registry } from 'vs/platform/registry/common/platform'; import { registerAction2, Action2, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { clamp } from 'vs/base/common/numbers'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; @@ -33,6 +33,9 @@ import { ResolutionResult, ResultKind } from 'vs/platform/keybinding/common/keyb import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IOutputService } from 'vs/workbench/services/output/common/output'; import { windowLogId } from 'vs/workbench/services/log/common/logConstants'; +import { ByteSize } from 'vs/platform/files/common/files'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; class InspectContextKeysAction extends Action2 { @@ -397,11 +400,118 @@ class LogWorkingCopiesAction extends Action2 { } } +class RemoveLargeStorageEntriesAction extends Action2 { + + private static SIZE_THRESHOLD = 1024 * 16; // 16kb + + constructor() { + super({ + id: 'workbench.action.removeLargeStorageDatabaseEntries', + title: { value: localize('removeLargeStorageDatabaseEntries', "Remove Large Storage Database Entries..."), original: 'Remove Large Storage Database Entries...' }, + category: Categories.Developer, + f1: true + }); + } + + async run(accessor: ServicesAccessor): Promise { + const storageService = accessor.get(IStorageService); + const quickInputService = accessor.get(IQuickInputService); + const userDataProfileService = accessor.get(IUserDataProfileService); + const dialogService = accessor.get(IDialogService); + + interface IStorageItem extends IQuickPickItem { + readonly key: string; + readonly scope: StorageScope; + readonly target: StorageTarget; + readonly size: number; + } + + const items: IStorageItem[] = []; + + for (const scope of [StorageScope.APPLICATION, StorageScope.PROFILE, StorageScope.WORKSPACE]) { + if (scope === StorageScope.PROFILE && userDataProfileService.currentProfile.isDefault) { + continue; // avoid duplicates + } + + for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { + for (const key of storageService.keys(scope, target)) { + const value = storageService.get(key, scope); + if (value && value.length > RemoveLargeStorageEntriesAction.SIZE_THRESHOLD) { + items.push({ + key, + scope, + target, + size: value.length, + label: key, + description: ByteSize.formatSize(value.length), + detail: localize('largeStorageItemDetail', "Scope: {0}, Target: {1}", scope === StorageScope.APPLICATION ? localize('global', "Global") : scope === StorageScope.PROFILE ? localize('profile', "Profile") : localize('workspace', "Workspace"), target === StorageTarget.MACHINE ? localize('machine', "Machine") : localize('user', "User")), + }); + } + } + } + } + + items.sort((itemA, itemB) => itemB.size - itemA.size); + + const selectedItems = await new Promise(resolve => { + const disposables = new DisposableStore(); + + const picker = disposables.add(quickInputService.createQuickPick()); + picker.items = items; + picker.canSelectMany = true; + picker.ok = false; + picker.customButton = true; + picker.hideCheckAll = true; + picker.customLabel = localize('removeLargeStorageEntriesPickerButton', "Remove"); + picker.placeholder = localize('removeLargeStorageEntriesPickerPlaceholder', "Select large entries to remove from storage"); + + if (items.length === 0) { + picker.description = localize('removeLargeStorageEntriesPickerDescriptionNoEntries', "There are no large storage entries to remove."); + } + + picker.show(); + + disposables.add(picker.onDidCustom(() => { + resolve(picker.selectedItems); + picker.hide(); + })); + + disposables.add(picker.onDidHide(() => disposables.dispose())); + }); + + if (selectedItems.length === 0) { + return; + } + + const { confirmed } = await dialogService.confirm({ + type: 'warning', + message: localize('removeLargeStorageEntriesConfirmRemove', "Do you want to remove the selected storage entries from the database?"), + detail: localize('removeLargeStorageEntriesConfirmRemoveDetail', "{0}\n\nThis action is irreversible and may result in data loss!", selectedItems.map(item => item.label).join('\n')), + primaryButton: localize({ key: 'removeLargeStorageEntriesButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Remove") + }); + + if (!confirmed) { + return; + } + + const scopesToOptimize = new Set(); + for (const item of selectedItems) { + storageService.remove(item.key, item.scope); + scopesToOptimize.add(item.scope); + } + + for (const scope of scopesToOptimize) { + await storageService.optimize(scope); + } + } +} + // --- Actions Registration registerAction2(InspectContextKeysAction); registerAction2(ToggleScreencastModeAction); registerAction2(LogStorageAction); registerAction2(LogWorkingCopiesAction); +registerAction2(RemoveLargeStorageEntriesAction); // --- Configuration diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 0f868b4ad97..af10ae9c109 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -525,10 +525,6 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro locationView.focus(); } - // if (options?.activate) { - // this.doSetGroupActive(group); - // } - return group; } diff --git a/src/vs/workbench/services/storage/browser/storageService.ts b/src/vs/workbench/services/storage/browser/storageService.ts index 5c55480dcdf..46b202086ae 100644 --- a/src/vs/workbench/services/storage/browser/storageService.ts +++ b/src/vs/workbench/services/storage/browser/storageService.ts @@ -432,6 +432,10 @@ export class IndexedDBStorageDatabase extends Disposable implements IIndexedDBSt return true; } + async optimize(): Promise { + // not suported in IndexedDB + } + async close(): Promise { const db = await this.whenConnected; From 7a0fecc0a739032cb35e4982bc5d8787b870e7cf Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 6 Sep 2023 09:36:50 +0200 Subject: [PATCH 540/607] #191860 - retry if command is not found --- test/automation/src/code.ts | 4 ++ test/automation/src/quickaccess.ts | 44 ++++++++++++++++--- .../src/areas/extensions/extensions.test.ts | 2 +- .../src/areas/workbench/localization.test.ts | 2 +- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index e8ad52540b5..4d0a74da4a8 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -254,6 +254,10 @@ export class Code { return this.driver.getLogs(); } + wait(millis: number): Promise { + return new Promise((resolve, reject) => setTimeout(resolve, millis)); + } + private async poll( fn: () => Promise, acceptFn: (result: T) => boolean, diff --git a/test/automation/src/quickaccess.ts b/test/automation/src/quickaccess.ts index 116f58b7747..a04ee52395c 100644 --- a/test/automation/src/quickaccess.ts +++ b/test/automation/src/quickaccess.ts @@ -171,15 +171,47 @@ export class QuickAccess { } async runCommand(commandId: string, keepOpen?: boolean): Promise { + let retries = 0; + let error; - // open commands picker - await this.openQuickAccessWithRetry(QuickAccessKind.Commands, `>${commandId}`); + while (true) { - // wait for best choice to be focused - await this.quickInput.waitForQuickInputElementFocused(); + if (++retries > 5) { + throw error ?? new Error(`Command: ${commandId} Not found`); + } + + // open commands picker + await this.openQuickAccessWithRetry(QuickAccessKind.Commands, `>${commandId}`); + + // wait for best choice to be focused + await this.quickInput.waitForQuickInputElementFocused(); + + // Retry for as long as the command not found + const text = await this.quickInput.waitForQuickInputElementText(); + if (text === 'No matching commands') { + this.code.logger.log(`QuickAccess: No matching commands, will retry...`); + await this.quickInput.closeQuickInput(); + await this.code.wait(1000); + continue; + } + + try { + // wait and click on best choice + await this.quickInput.selectQuickInputElement(0, keepOpen); + } catch (err) { + if (keepOpen) { + throw err; + } else { + error = err; + this.code.logger.log(`QuickAccess: Probably no matching commands, will retry...`); + await this.quickInput.closeQuickInput(); + continue; + } + } + + break; + } - // wait and click on best choice - await this.quickInput.selectQuickInputElement(0, keepOpen); } async openQuickOutline(): Promise { diff --git a/test/smoke/src/areas/extensions/extensions.test.ts b/test/smoke/src/areas/extensions/extensions.test.ts index bc65a8631c4..c78cbe87089 100644 --- a/test/smoke/src/areas/extensions/extensions.test.ts +++ b/test/smoke/src/areas/extensions/extensions.test.ts @@ -7,7 +7,7 @@ import { Application, Logger } from '../../../../automation'; import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { - describe.skip('Extensions', () => { + describe('Extensions', () => { // Shared before/after handling installAllHandlers(logger); diff --git a/test/smoke/src/areas/workbench/localization.test.ts b/test/smoke/src/areas/workbench/localization.test.ts index 865add9ae79..12e49ce549e 100644 --- a/test/smoke/src/areas/workbench/localization.test.ts +++ b/test/smoke/src/areas/workbench/localization.test.ts @@ -8,7 +8,7 @@ import { installAllHandlers } from '../../utils'; export function setup(logger: Logger) { - describe.skip('Localization', () => { + describe('Localization', () => { // Shared before/after handling installAllHandlers(logger); From ab2afd626faa8660a51f45798640d3c00f944a59 Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 6 Sep 2023 16:58:16 +0900 Subject: [PATCH 541/607] chore: disable hardware acceleration support for linux smoke tests (#192265) --- test/automation/src/electron.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index 1fea286c6cf..4f1cb9b27e5 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -47,6 +47,8 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom // this partition for shared memory. // Refs https://github.com/microsoft/vscode/issues/152143 args.push('--disable-dev-shm-usage'); + // Refs https://github.com/microsoft/vscode/issues/192206 + args.push('--disable-gpu'); } if (process.platform === 'darwin') { From e5d65965c3a8df213e2c412875f57b0601398ca9 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 6 Sep 2023 10:01:39 +0200 Subject: [PATCH 542/607] adding the fix for leaking disposables --- .../documentSymbols/browser/outlineModel.ts | 1 + .../test/browser/stickyScroll.test.ts | 21 +++++++++++++++---- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts index 61caa1c30fa..1d5e97e363d 100644 --- a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts @@ -234,6 +234,7 @@ export class OutlineModel extends TreeElement { return result._compact(); } }).finally(() => { + cts.dispose(); listener.dispose(); cts.dispose(); }); diff --git a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts index 906d1299bb8..9f15b6ad681 100644 --- a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts +++ b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts @@ -21,9 +21,13 @@ import { TestLanguageConfigurationService } from 'vs/editor/test/common/modes/te import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Sticky Scroll Tests', () => { + const disposables = new DisposableStore(); + const serviceCollection = new ServiceCollection( [ILanguageFeaturesService, new LanguageFeaturesService()], [ILogService, new NullLogService()], @@ -53,6 +57,15 @@ suite('Sticky Scroll Tests', () => { '}' ].join('\n'); + setup(() => { + disposables.clear(); + }); + teardown(() => { + disposables.clear(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + function documentSymbolProviderForTestModel() { return { provideDocumentSymbols() { @@ -128,7 +141,7 @@ suite('Sticky Scroll Tests', () => { }, async (editor, _viewModel, instantiationService) => { const languageService = instantiationService.get(ILanguageFeaturesService); const languageConfigurationService = instantiationService.get(ILanguageConfigurationService); - languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel()); + disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel())); const provider: StickyLineCandidateProvider = new StickyLineCandidateProvider(editor, languageService, languageConfigurationService); await provider.update(); assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 4 }), [new StickyLineCandidate(1, 2, 1)]); @@ -155,7 +168,7 @@ suite('Sticky Scroll Tests', () => { const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController); const lineHeight: number = editor.getOption(EditorOption.lineHeight); const languageService: ILanguageFeaturesService = instantiationService.get(ILanguageFeaturesService); - languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel()); + disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel())); await stickyScrollController.stickyScrollCandidateProvider.update(); let state; @@ -205,7 +218,7 @@ suite('Sticky Scroll Tests', () => { const lineHeight = editor.getOption(EditorOption.lineHeight); const languageService = instantiationService.get(ILanguageFeaturesService); - languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel()); + disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel())); await stickyScrollController.stickyScrollCandidateProvider.update(); editor.setHiddenAreas([{ startLineNumber: 2, endLineNumber: 2, startColumn: 1, endColumn: 1 }, { startLineNumber: 10, endLineNumber: 11, startColumn: 1, endColumn: 1 }]); let state; @@ -300,7 +313,7 @@ suite('Sticky Scroll Tests', () => { const lineHeight = editor.getOption(EditorOption.lineHeight); const languageService = instantiationService.get(ILanguageFeaturesService); - languageService.documentSymbolProvider.register('*', documentSymbolProviderForSecondTestModel()); + disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForSecondTestModel())); await stickyScrollController.stickyScrollCandidateProvider.update(); let state; From acfce3c8c442c0ca001daf2f05de7ec63d6d9fe9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 6 Sep 2023 10:08:22 +0200 Subject: [PATCH 543/607] Get settings sync session only when needed (#192264) #187284 Get settings sync session only when needed --- .../userDataSync/browser/userDataSync.ts | 4 +- .../browser/userDataSyncWorkbenchService.ts | 166 ++++++++++-------- .../userDataSync/common/userDataSync.ts | 4 +- 3 files changed, 91 insertions(+), 83 deletions(-) diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index e50bd3e8559..e037a4298f8 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -716,7 +716,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private registerTurnOnSyncAction(): void { const that = this; - const when = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_ACCOUNT_STATE.notEqualsTo(AccountStatus.Uninitialized), CONTEXT_TURNING_ON_STATE.negate()); + const when = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_TURNING_ON_STATE.negate()); this._register(registerAction2(class TurningOnSyncAction extends Action2 { constructor() { super({ @@ -750,7 +750,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private registerTurningOnSyncAction(): void { - const when = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_ACCOUNT_STATE.notEqualsTo(AccountStatus.Uninitialized), CONTEXT_TURNING_ON_STATE); + const when = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_ENABLEMENT.toNegated(), CONTEXT_TURNING_ON_STATE); this._register(registerAction2(class TurningOnSyncAction extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 4909ab5d1a4..71cbb86e4d7 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -9,7 +9,6 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { IUserDataSyncWorkbenchService, IUserDataSyncAccount, AccountStatus, CONTEXT_SYNC_ENABLEMENT, CONTEXT_SYNC_STATE, CONTEXT_ACCOUNT_STATE, SHOW_SYNC_LOG_COMMAND_ID, CONTEXT_ENABLE_ACTIVITY_VIEWS, SYNC_VIEW_CONTAINER_ID, SYNC_TITLE, SYNC_CONFLICTS_VIEW_ID, CONTEXT_ENABLE_SYNC_CONFLICTS_VIEW, CONTEXT_HAS_CONFLICTS, IUserDataSyncConflictsView } from 'vs/workbench/services/userDataSync/common/userDataSync'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; -import { flatten } from 'vs/base/common/arrays'; import { getCurrentAuthenticationSessionInfo } from 'vs/workbench/services/authentication/browser/authenticationService'; import { AuthenticationSession, AuthenticationSessionsChangeEvent, IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication'; import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; @@ -71,15 +70,13 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat private _authenticationProviders: IAuthenticationProvider[] = []; get authenticationProviders() { return this._authenticationProviders; } - private _accountStatus: AccountStatus = AccountStatus.Uninitialized; + private _accountStatus: AccountStatus = AccountStatus.Unavailable; get accountStatus(): AccountStatus { return this._accountStatus; } private readonly _onDidChangeAccountStatus = this._register(new Emitter()); readonly onDidChangeAccountStatus = this._onDidChangeAccountStatus.event; - private _all: Map = new Map(); - get all(): UserDataSyncAccount[] { return flatten([...this._all.values()]); } - - get current(): UserDataSyncAccount | undefined { return this.all.filter(account => this.isCurrentAccount(account))[0]; } + private _current: UserDataSyncAccount | undefined; + get current(): UserDataSyncAccount | undefined { return this._current; } private readonly syncEnablementContext: IContextKey; private readonly syncStatusContext: IContextKey; @@ -149,7 +146,6 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat /* initialize */ try { - this.logService.trace('Settings Sync: Initializing accounts'); await this.initialize(); } catch (error) { // Do not log if the current window is running extension tests @@ -157,15 +153,6 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat this.logService.error(error); } } - - if (this.accountStatus === AccountStatus.Uninitialized) { - // Do not log if the current window is running extension tests - if (!this.environmentService.extensionTestsLocationURI) { - this.logService.warn('Settings Sync: Accounts are not initialized'); - } - } else { - this.logService.trace('Settings Sync: Accounts are initialized'); - } } private async initialize(): Promise { @@ -221,43 +208,32 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat private async update(): Promise { this.updateAuthenticationProviders(); + await this.updateCurrentAccount(); - const allAccounts: Map = new Map(); - for (const { id, scopes } of this.authenticationProviders) { - this.logService.trace('Settings Sync: Getting accounts for', id); - const accounts = await this.getAccounts(id, scopes); - allAccounts.set(id, accounts); - this.logService.trace('Settings Sync: Updated accounts for', id); + if (this._current) { + this.currentAuthenticationProviderId = this._current.authenticationProviderId; } - this._all = allAccounts; - const current = this.current; - if (current) { - this.currentAuthenticationProviderId = current.authenticationProviderId; - } - await this.updateToken(current); - this.updateAccountStatus(current ? AccountStatus.Available : AccountStatus.Unavailable); + await this.updateToken(this._current); + this.updateAccountStatus(this._current ? AccountStatus.Available : AccountStatus.Unavailable); } - private async getAccounts(authenticationProviderId: string, scopes: string[]): Promise { - const accounts: Map = new Map(); - let currentAccount: UserDataSyncAccount | null = null; - - const sessions = await this.authenticationService.getSessions(authenticationProviderId, scopes) || []; - for (const session of sessions) { - const account: UserDataSyncAccount = new UserDataSyncAccount(authenticationProviderId, session); - accounts.set(account.accountId, account); - if (this.isCurrentAccount(account)) { - currentAccount = account; + private async updateCurrentAccount(): Promise { + const currentSessionId = this.currentSessionId; + const currentAuthenticationProviderId = this.currentAuthenticationProviderId; + if (currentSessionId) { + const authenticationProviders = currentAuthenticationProviderId ? this.authenticationProviders.filter(({ id }) => id === currentAuthenticationProviderId) : this.authenticationProviders; + for (const { id, scopes } of authenticationProviders) { + const sessions = (await this.authenticationService.getSessions(id, scopes)) || []; + for (const session of sessions) { + if (session.id === currentSessionId) { + this._current = new UserDataSyncAccount(id, session); + return; + } + } } } - - if (currentAccount) { - // Always use current account if available - accounts.set(currentAccount.accountId, currentAccount); - } - - return [...accounts.values()]; + this._current = undefined; } private async updateToken(current: UserDataSyncAccount | undefined): Promise { @@ -486,10 +462,6 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } } - private isCurrentAccount(account: UserDataSyncAccount): boolean { - return account.sessionId === this.currentSessionId; - } - async signIn(): Promise { const currentAuthenticationProviderId = this.currentAuthenticationProviderId; const authenticationProvider = currentAuthenticationProviderId ? this.authenticationProviders.find(p => p.id === currentAuthenticationProviderId) : undefined; @@ -514,46 +486,85 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat return undefined; } - await this.update(); + const authenticationProviders = [...this.authenticationProviders].sort(({ id }) => id === this.currentAuthenticationProviderId ? -1 : 1); + const allAccounts = new Map(); - // Single auth provider and no accounts available - if (this.authenticationProviders.length === 1 && !this.all.length) { - return this.authenticationProviders[0]; + if (authenticationProviders.length === 1) { + const accounts = await this.getAccounts(authenticationProviders[0].id, authenticationProviders[0].scopes); + if (accounts.length) { + allAccounts.set(authenticationProviders[0].id, accounts); + } else { + // Single auth provider and no accounts + return authenticationProviders[0]; + } } - return new Promise(c => { - let result: UserDataSyncAccount | IAuthenticationProvider | undefined; - const disposables: DisposableStore = new DisposableStore(); - const quickPick = this.quickInputService.createQuickPick(); - disposables.add(quickPick); + let result: UserDataSyncAccount | IAuthenticationProvider | undefined; + const disposables: DisposableStore = new DisposableStore(); + const quickPick = disposables.add(this.quickInputService.createQuickPick()); - quickPick.title = SYNC_TITLE; - quickPick.ok = false; - quickPick.placeholder = localize('choose account placeholder', "Select an account to sign in"); - quickPick.ignoreFocusOut = true; - quickPick.items = this.createQuickpickItems(); - - disposables.add(quickPick.onDidAccept(() => { - result = quickPick.selectedItems[0]?.account ? quickPick.selectedItems[0]?.account : quickPick.selectedItems[0]?.authenticationProvider; - quickPick.hide(); - })); + const promise = new Promise(c => { disposables.add(quickPick.onDidHide(() => { disposables.dispose(); c(result); })); - quickPick.show(); }); + + quickPick.title = SYNC_TITLE; + quickPick.ok = false; + quickPick.ignoreFocusOut = true; + quickPick.placeholder = localize('choose account placeholder', "Select an account to sign in"); + quickPick.show(); + + if (authenticationProviders.length > 1) { + quickPick.busy = true; + for (const { id, scopes } of authenticationProviders) { + const accounts = await this.getAccounts(id, scopes); + if (accounts.length) { + allAccounts.set(id, accounts); + } + } + quickPick.busy = false; + } + + quickPick.items = this.createQuickpickItems(authenticationProviders, allAccounts); + disposables.add(quickPick.onDidAccept(() => { + result = quickPick.selectedItems[0]?.account ? quickPick.selectedItems[0]?.account : quickPick.selectedItems[0]?.authenticationProvider; + quickPick.hide(); + })); + + return promise; } - private createQuickpickItems(): (AccountQuickPickItem | IQuickPickSeparator)[] { + private async getAccounts(authenticationProviderId: string, scopes: string[]): Promise { + const accounts: Map = new Map(); + let currentAccount: UserDataSyncAccount | null = null; + + const sessions = await this.authenticationService.getSessions(authenticationProviderId, scopes) || []; + for (const session of sessions) { + const account: UserDataSyncAccount = new UserDataSyncAccount(authenticationProviderId, session); + accounts.set(account.accountId, account); + if (account.sessionId === this.currentSessionId) { + currentAccount = account; + } + } + + if (currentAccount) { + // Always use current account if available + accounts.set(currentAccount.accountId, currentAccount); + } + + return currentAccount ? [...accounts.values()] : [...accounts.values()].sort(({ sessionId }) => sessionId === this.currentSessionId ? -1 : 1); + } + + private createQuickpickItems(authenticationProviders: IAuthenticationProvider[], allAccounts: Map): (AccountQuickPickItem | IQuickPickSeparator)[] { const quickPickItems: (AccountQuickPickItem | IQuickPickSeparator)[] = []; // Signed in Accounts - if (this.all.length) { - const authenticationProviders = [...this.authenticationProviders].sort(({ id }) => id === this.current?.authenticationProviderId ? -1 : 1); + if (allAccounts.size) { quickPickItems.push({ type: 'separator', label: localize('signed in', "Signed in") }); for (const authenticationProvider of authenticationProviders) { - const accounts = (this._all.get(authenticationProvider.id) || []).sort(({ sessionId }) => sessionId === this.current?.sessionId ? -1 : 1); + const accounts = (allAccounts.get(authenticationProvider.id) || []).sort(({ sessionId }) => sessionId === this.currentSessionId ? -1 : 1); const providerName = this.authenticationService.getLabel(authenticationProvider.id); for (const account of accounts) { quickPickItems.push({ @@ -567,10 +578,9 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat quickPickItems.push({ type: 'separator', label: localize('others', "Others") }); } - // Account proviers - for (const authenticationProvider of this.authenticationProviders) { - const signedInForProvider = this.all.some(account => account.authenticationProviderId === authenticationProvider.id); - if (!signedInForProvider || this.authenticationService.supportsMultipleAccounts(authenticationProvider.id)) { + // Account Providers + for (const authenticationProvider of authenticationProviders) { + if (!allAccounts.has(authenticationProvider.id) || this.authenticationService.supportsMultipleAccounts(authenticationProvider.id)) { const providerName = this.authenticationService.getLabel(authenticationProvider.id); quickPickItems.push({ label: localize('sign in using account', "Sign in with {0}", providerName), authenticationProvider }); } diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts index 1cfcc42cc02..ace123f1533 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts @@ -26,7 +26,6 @@ export interface IUserDataSyncWorkbenchService { readonly enabled: boolean; readonly authenticationProviders: IAuthenticationProvider[]; - readonly all: IUserDataSyncAccount[]; readonly current: IUserDataSyncAccount | undefined; readonly accountStatus: AccountStatus; @@ -60,7 +59,6 @@ export function getSyncAreaLabel(source: SyncResource): string { } export const enum AccountStatus { - Uninitialized = 'uninitialized', Unavailable = 'unavailable', Available = 'available', } @@ -77,7 +75,7 @@ export const SYNC_VIEW_ICON = registerIcon('settings-sync-view-icon', Codicon.sy // Contexts export const CONTEXT_SYNC_STATE = new RawContextKey('syncStatus', SyncStatus.Uninitialized); export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey('syncEnabled', false); -export const CONTEXT_ACCOUNT_STATE = new RawContextKey('userDataSyncAccountStatus', AccountStatus.Uninitialized); +export const CONTEXT_ACCOUNT_STATE = new RawContextKey('userDataSyncAccountStatus', AccountStatus.Unavailable); export const CONTEXT_ENABLE_ACTIVITY_VIEWS = new RawContextKey(`enableSyncActivityViews`, false); export const CONTEXT_ENABLE_SYNC_CONFLICTS_VIEW = new RawContextKey(`enableSyncConflictsView`, false); export const CONTEXT_HAS_CONFLICTS = new RawContextKey('hasConflicts', false); From 6e93ce037bb242119c9ccb8f43ae999bfb37ca7b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 6 Sep 2023 10:30:13 +0200 Subject: [PATCH 544/607] feedback --- test/automation/src/code.ts | 6 +++--- test/automation/src/playwrightDriver.ts | 6 +++--- test/automation/src/terminal.ts | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 4d0a74da4a8..1647e78640b 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -185,7 +185,7 @@ export class Code { try { process.kill(pid, 0); // throws an exception if the process doesn't exist anymore. - await new Promise(resolve => setTimeout(resolve, 500)); + await this.wait(500); } catch (error) { done = true; resolve(); @@ -255,7 +255,7 @@ export class Code { } wait(millis: number): Promise { - return new Promise((resolve, reject) => setTimeout(resolve, millis)); + return this.driver.wait(millis); } private async poll( @@ -289,7 +289,7 @@ export class Code { lastError = Array.isArray(e.stack) ? e.stack.join(os.EOL) : e.stack; } - await new Promise(resolve => setTimeout(resolve, retryInterval)); + await this.wait(retryInterval); trial++; } } diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 1b63f622f4e..ddc99579fdf 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -157,7 +157,7 @@ export class PlaywrightDriver { for (let i = 0; i < chords.length; i++) { const chord = chords[i]; if (i > 0) { - await this.timeout(100); + await this.wait(100); } if (keybinding.startsWith('Alt') || keybinding.startsWith('Control') || keybinding.startsWith('Backspace')) { @@ -179,7 +179,7 @@ export class PlaywrightDriver { } } - await this.timeout(100); + await this.wait(100); } async click(selector: string, xoffset?: number | undefined, yoffset?: number | undefined) { @@ -235,7 +235,7 @@ export class PlaywrightDriver { return this.page.evaluate(pageFunction, [await this.getDriverHandle()]); } - private timeout(ms: number): Promise { + wait(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts index 56b6f46ddb4..e0604a5b45f 100644 --- a/test/automation/src/terminal.ts +++ b/test/automation/src/terminal.ts @@ -99,7 +99,7 @@ export class Terminal { // after 2 seconds. await Promise.race([ this.code.waitForElements(Selector.Xterm, true, e => e.length === 0), - new Promise(r => setTimeout(r, 2000)) + this.code.wait(2000) ]); break; } From 2e33655e9c34db7011a84a09879aef676e0bc813 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 6 Sep 2023 10:39:10 +0200 Subject: [PATCH 545/607] rename variables title height, added super methods --- .../lib/stylelint/vscode-known-variables.json | 2 +- .../parts/editor/media/notabstitlecontrol.css | 6 ++--- .../parts/editor/media/tabstitlecontrol.css | 8 +++---- .../parts/editor/media/titlecontrol.css | 2 +- .../parts/editor/noTabsTitleControl.ts | 13 +++++------ .../browser/parts/editor/tabsTitleControl.ts | 13 ++++++----- .../browser/parts/editor/titleControl.ts | 22 ++++++++++++++----- 7 files changed, 37 insertions(+), 29 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 62b9f2fc563..f10da52142c 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -734,7 +734,7 @@ "--tab-sizing-current-width", "--tab-sizing-fixed-min-width", "--tab-sizing-fixed-max-width", - "--tab-height", + "--title-height", "--testMessageDecorationFontFamily", "--testMessageDecorationFontSize", "--title-border-bottom-color", diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index b737c53817c..5bfc46c255c 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -6,7 +6,7 @@ /* Title Label */ .monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container { - height: var(--tab-height); + height: var(--title-height); display: flex; justify-content: flex-start; align-items: center; @@ -15,7 +15,7 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container > .title-label { - line-height: var(--tab-height); + line-height: var(--title-height); overflow: hidden; text-overflow: ellipsis; position: relative; @@ -94,7 +94,7 @@ flex: initial; opacity: 0.5; padding-right: 8px; - height: var(--tab-height); + height: var(--title-height); } .monaco-workbench .part.editor > .content .editor-group-container > .title > .title-actions .action-item { diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 62de2b2dc11..68375000e58 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -71,7 +71,7 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container { display: flex; - height: var(--tab-height); + height: var(--title-height); scrollbar-width: none; /* Firefox: hide scrollbar */ } @@ -97,7 +97,7 @@ display: flex; white-space: nowrap; cursor: pointer; - height: var(--tab-height); + height: var(--title-height); box-sizing: border-box; padding-left: 10px; } @@ -265,7 +265,7 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { margin-top: auto; margin-bottom: auto; - line-height: var(--tab-height); /* aligns icon and label vertically centered in the tab */ + line-height: var(--title-height); /* aligns icon and label vertically centered in the tab */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label, @@ -473,7 +473,7 @@ cursor: default; flex: initial; padding: 0 8px 0 4px; - height: var(--tab-height); + height: var(--title-height); } .monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-item { diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index d627fc05a5b..2ab7ffcc5c3 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -30,7 +30,7 @@ } .monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before { - height: var(--tab-height); /* tweak the icon size of the editor labels when icons are enabled */ + height: var(--title-height); /* tweak the icon size of the editor labels when icons are enabled */ } .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .monaco-icon-label::after, diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index bc4e3b25e21..0772d57a3ce 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -32,7 +32,9 @@ export class NoTabsTitleControl extends TitleControl { private editorLabel: IResourceLabel | undefined; private activeLabel: IRenderedEditorLabel = Object.create(null); - protected create(parent: HTMLElement): void { + protected override create(parent: HTMLElement): void { + super.create(parent); + const titleContainer = this.titleContainer = parent; titleContainer.draggable = true; @@ -50,8 +52,6 @@ export class NoTabsTitleControl extends TitleControl { this.editorLabel = this._register(this.instantiationService.createInstance(ResourceLabel, labelContainer, undefined)).element; this._register(addDisposableListener(this.editorLabel.element, EventType.CLICK, e => this.onTitleLabelClick(e))); - this.updateTabHeight(); - // Breadcrumbs this.createBreadcrumbsControl(labelContainer, { showFileIcons: false, showSymbolIcons: true, showDecorationColors: false, widgetStyles: { ...defaultBreadcrumbsWidgetStyles, breadcrumbsBackground: Color.transparent.toString() }, showPlaceholder: false }); titleContainer.classList.toggle('breadcrumbs', Boolean(this.breadcrumbsControl)); @@ -199,11 +199,8 @@ export class NoTabsTitleControl extends TitleControl { }); } - updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { - // Update tab height - if (oldOptions.tabHeight !== newOptions.tabHeight) { - this.updateTabHeight(); - } + override updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { + super.updateOptions(oldOptions, newOptions); if (oldOptions.labelFormat !== newOptions.labelFormat || !equals(oldOptions.decorations, newOptions.decorations)) { this.redraw(); diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index e331f0e4945..ed3875f0cd5 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -163,7 +163,9 @@ export class TabsTitleControl extends TitleControl { this._register(this.tabResourceLabels.onDidChangeDecorations(() => this.doHandleDecorationsChange())); } - protected create(parent: HTMLElement): void { + protected override create(parent: HTMLElement): void { + super.create(parent); + this.titleContainer = parent; // Tabs and Actions Container (are on a single row with flex side-by-side) @@ -180,7 +182,6 @@ export class TabsTitleControl extends TitleControl { this.tabSizingFixedDisposables = this._register(new DisposableStore()); this.updateTabSizing(false); - this.updateTabHeight(); // Tabs Scrollbar this.tabsScrollbar = this.createTabsScrollbar(this.tabsContainer); @@ -202,6 +203,7 @@ export class TabsTitleControl extends TitleControl { breadcrumbsContainer.classList.add('tabs-breadcrumbs'); this.titleContainer.appendChild(breadcrumbsContainer); this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, showPlaceholder: true }); + } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { @@ -268,7 +270,6 @@ export class TabsTitleControl extends TitleControl { }); } - private getTabsScrollbarSizing(): number { if (this.accessor.partOptions.titleScrollbarSizing !== 'large') { return TabsTitleControl.SCROLLBAR_SIZES.default; @@ -710,7 +711,8 @@ export class TabsTitleControl extends TitleControl { this.withTab(editor, (editor, index, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => this.redrawTabActiveAndDirty(this.accessor.activeGroup === this.group, editor, tabContainer, tabActionBar)); } - updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { + override updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { + super.updateOptions(oldOptions, newOptions); // A change to a label format options requires to recompute all labels if (oldOptions.labelFormat !== newOptions.labelFormat) { @@ -733,7 +735,7 @@ export class TabsTitleControl extends TitleControl { // Update tab height if (oldOptions.tabHeight !== newOptions.tabHeight) { - this.updateTabHeight(); + this.updateTitleHeight(); } // Redraw tabs when other options change @@ -1567,7 +1569,6 @@ export class TabsTitleControl extends TitleControl { return { total, offset }; } - layout(dimensions: ITitleControlDimensions, options?: ITabsTitleControlLayoutOptions): Dimension { // Remember dimensions that we get diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 352258bb80a..e3a17ba3705 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -93,6 +93,9 @@ export abstract class TitleControl extends Themable { protected readonly groupTransfer = LocalSelectionTransfer.getInstance(); protected readonly treeItemsTransfer = LocalSelectionTransfer.getInstance(); + private static readonly EDITOR_TITLE_NORMAL = 35; + private static readonly EDITOR_TITLE_COMPACT = 22; + protected breadcrumbsControl: BreadcrumbsControl | undefined = undefined; private editorActionsToolbar: WorkbenchToolBar | undefined; @@ -150,7 +153,9 @@ export abstract class TitleControl extends Themable { this.create(parent); } - protected abstract create(parent: HTMLElement): void; + protected create(parent: HTMLElement): void { + this.updateTitleHeight(); + } protected createBreadcrumbsControl(container: HTMLElement, options: IBreadcrumbsControlOptions): void { const config = this._register(BreadcrumbsConfig.IsEnabled.bindTo(this.configurationService)); @@ -423,11 +428,18 @@ export abstract class TitleControl extends Themable { } protected get tabHeight() { - return this.accessor.partOptions.tabHeight !== 'compact' ? 35 : 22; + return this.accessor.partOptions.tabHeight !== 'compact' ? TitleControl.EDITOR_TITLE_NORMAL : TitleControl.EDITOR_TITLE_COMPACT; } - protected updateTabHeight(): void { - this.parent.style.setProperty('--tab-height', `${this.tabHeight}px`); + protected updateTitleHeight(): void { + this.parent.style.setProperty('--title-height', `${this.tabHeight}px`); + } + + updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { + // Update title height + if (oldOptions.tabHeight !== newOptions.tabHeight) { + this.updateTitleHeight(); + } } abstract openEditor(editor: EditorInput): void; @@ -454,8 +466,6 @@ export abstract class TitleControl extends Themable { abstract updateEditorDirty(editor: EditorInput): void; - abstract updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void; - abstract layout(dimensions: ITitleControlDimensions): Dimension; abstract getHeight(): IEditorGroupTitleHeight; From 4adde92102c499bfcb2eb5c169e08b1eb73f2d9b Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 6 Sep 2023 10:57:28 +0200 Subject: [PATCH 546/607] command to browse marketplace color themes (#192269) --- .../themes/browser/themes.contribution.ts | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 45243cdcf21..f245ba5a9bc 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -662,6 +662,55 @@ registerAction2(class extends Action2 { } }); +const browseColorThemesInMarketplaceCommandId = 'workbench.action.browseColorThemesInMarketplace'; + +registerAction2(class extends Action2 { + + constructor() { + super({ + id: browseColorThemesInMarketplaceCommandId, + title: { value: localize('browseColorThemeInMarketPlace.label', "Browse Color Themes in Marketplace"), original: 'Browse Color Themes in Marketplace' }, + category: Categories.Preferences, + f1: true, + }); + } + + override async run(accessor: ServicesAccessor) { + const marketplaceTag = 'category:themes'; + const themeService = accessor.get(IWorkbenchThemeService); + const extensionGalleryService = accessor.get(IExtensionGalleryService); + const extensionResourceLoaderService = accessor.get(IExtensionResourceLoaderService); + const instantiationService = accessor.get(IInstantiationService); + + if (!extensionGalleryService.isEnabled() || !extensionResourceLoaderService.supportsExtensionGalleryResources) { + return; + } + const currentTheme = themeService.getColorTheme(); + const getMarketplaceColorThemes = (publisher: string, name: string, version: string) => themeService.getMarketplaceColorThemes(publisher, name, version); + + let selectThemeTimeout: number | undefined; + + const selectTheme = (theme: IWorkbenchTheme | undefined, applyTheme: boolean) => { + if (selectThemeTimeout) { + clearTimeout(selectThemeTimeout); + } + selectThemeTimeout = window.setTimeout(() => { + selectThemeTimeout = undefined; + const newTheme = (theme ?? currentTheme) as IWorkbenchTheme; + themeService.setColorTheme(newTheme as IWorkbenchColorTheme, applyTheme ? 'auto' : 'preview').then(undefined, + err => { + onUnexpectedError(err); + themeService.setColorTheme(currentTheme, undefined); + } + ); + }, applyTheme ? 0 : 200); + }; + + const marketplaceThemePicker = instantiationService.createInstance(MarketplaceThemesPicker, getMarketplaceColorThemes, marketplaceTag); + await marketplaceThemePicker.openQuickPick('', themeService.getColorTheme(), selectTheme).then(undefined, onUnexpectedError); + } +}); + const ThemesSubMenu = new MenuId('ThemesSubMenu'); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { title: localize('themes', "Themes"), From 7b15902db0038e96bda37b9ff5e0347e05d707da Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 6 Sep 2023 10:58:27 +0200 Subject: [PATCH 547/607] only check for No matching commands text --- test/automation/src/quickaccess.ts | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/test/automation/src/quickaccess.ts b/test/automation/src/quickaccess.ts index a04ee52395c..67b4a6ad289 100644 --- a/test/automation/src/quickaccess.ts +++ b/test/automation/src/quickaccess.ts @@ -172,13 +172,8 @@ export class QuickAccess { async runCommand(commandId: string, keepOpen?: boolean): Promise { let retries = 0; - let error; - while (true) { - - if (++retries > 5) { - throw error ?? new Error(`Command: ${commandId} Not found`); - } + while (++retries > 5) { // open commands picker await this.openQuickAccessWithRetry(QuickAccessKind.Commands, `>${commandId}`); @@ -195,23 +190,14 @@ export class QuickAccess { continue; } - try { - // wait and click on best choice - await this.quickInput.selectQuickInputElement(0, keepOpen); - } catch (err) { - if (keepOpen) { - throw err; - } else { - error = err; - this.code.logger.log(`QuickAccess: Probably no matching commands, will retry...`); - await this.quickInput.closeQuickInput(); - continue; - } - } + // wait and click on best choice + await this.quickInput.selectQuickInputElement(0, keepOpen); - break; + return; } + throw new Error(`Command: ${commandId} Not found`); + } async openQuickOutline(): Promise { From 9ed384ffe76b139f65a50c111fbbd5cfb947b73b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 6 Sep 2023 11:10:35 +0200 Subject: [PATCH 548/607] fix while check --- test/automation/src/quickaccess.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/automation/src/quickaccess.ts b/test/automation/src/quickaccess.ts index 67b4a6ad289..4c0f1079db1 100644 --- a/test/automation/src/quickaccess.ts +++ b/test/automation/src/quickaccess.ts @@ -173,7 +173,7 @@ export class QuickAccess { async runCommand(commandId: string, keepOpen?: boolean): Promise { let retries = 0; - while (++retries > 5) { + while (++retries < 5) { // open commands picker await this.openQuickAccessWithRetry(QuickAccessKind.Commands, `>${commandId}`); From 55abf1577aa1276a2d8a8b0ad7f8fcee13b2aeed Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 6 Sep 2023 11:17:52 +0200 Subject: [PATCH 549/607] a bit more `ensureNoDisposablesAreLeakedInTestSuite` work --- .../test/browser/suggestInlineCompletions.test.ts | 4 ++++ .../platform/commands/test/common/commands.test.ts | 11 ++++++++--- .../bulkEdit/test/browser/bulkEditPreview.test.ts | 7 +++++++ .../contrib/chat/test/common/chatVariables.test.ts | 10 ++++++++-- .../test/browser/decorationsService.test.ts | 8 +++++++- .../browser/parts/editor/breadcrumbModel.test.ts | 14 +++++++++++--- 6 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts index b8e16dc8ef3..c80ef6fed15 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts @@ -8,6 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { CompletionContext, CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, InlineCompletionTriggerKind, ProviderResult } from 'vs/editor/common/languages'; @@ -71,6 +72,8 @@ suite('Suggest Inline Completions', function () { }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('Aggressive inline completions when typing within line #146948', async function () { const completions: SuggestInlineCompletions = insta.createInstance(SuggestInlineCompletions, (id) => editor.getOption(id)); @@ -79,6 +82,7 @@ suite('Suggest Inline Completions', function () { // (1,3), end of word -> suggestions const result = await completions.provideInlineCompletions(model, new Position(1, 3), { triggerKind: InlineCompletionTriggerKind.Explicit, selectedSuggestionInfo: undefined }, CancellationToken.None); assert.strictEqual(result?.items.length, 3); + completions.freeInlineCompletions(result); } { // (1,2), middle of word -> NO suggestions diff --git a/src/vs/platform/commands/test/common/commands.test.ts b/src/vs/platform/commands/test/common/commands.test.ts index bc7e0c7b276..7dcb65de2d4 100644 --- a/src/vs/platform/commands/test/common/commands.test.ts +++ b/src/vs/platform/commands/test/common/commands.test.ts @@ -3,10 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { combinedDisposable } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; suite('Command Tests', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + test('register command - no handler', function () { assert.throws(() => CommandsRegistry.registerCommand('foo', null!)); }); @@ -49,15 +53,15 @@ suite('Command Tests', function () { test('command with description', function () { - CommandsRegistry.registerCommand('test', function (accessor, args) { + const r1 = CommandsRegistry.registerCommand('test', function (accessor, args) { assert.ok(typeof args === 'string'); }); - CommandsRegistry.registerCommand('test2', function (accessor, args) { + const r2 = CommandsRegistry.registerCommand('test2', function (accessor, args) { assert.ok(typeof args === 'string'); }); - CommandsRegistry.registerCommand({ + const r3 = CommandsRegistry.registerCommand({ id: 'test3', handler: function (accessor, args) { return true; @@ -73,5 +77,6 @@ suite('Command Tests', function () { assert.throws(() => CommandsRegistry.getCommands().get('test3')!.handler.apply(undefined, [undefined!, 'string'])); assert.strictEqual(CommandsRegistry.getCommands().get('test3')!.handler.apply(undefined, [undefined!, 1]), true); + combinedDisposable(r1, r2, r3).dispose(); }); }); diff --git a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts index bd00c579e85..a05e30502cc 100644 --- a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts +++ b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts @@ -15,9 +15,11 @@ import { URI } from 'vs/base/common/uri'; import { BulkFileOperations } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; import { Range } from 'vs/editor/common/core/range'; import { ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('BulkEditPreview', function () { + const store = ensureNoDisposablesAreLeakedInTestSuite(); let instaService: IInstantiationService; @@ -53,6 +55,7 @@ suite('BulkEditPreview', function () { ]; const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); + store.add(ops); assert.strictEqual(ops.fileOperations.length, 1); assert.strictEqual(ops.checked.isChecked(edits[0]), false); }); @@ -66,6 +69,7 @@ suite('BulkEditPreview', function () { const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); + store.add(ops); assert.strictEqual(ops.categories.length, 2); assert.strictEqual(ops.categories[0].metadata.label, 'uri1'); // unconfirmed! assert.strictEqual(ops.categories[1].metadata.label, 'uri2'); @@ -79,6 +83,7 @@ suite('BulkEditPreview', function () { ]; const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); + store.add(ops); assert.strictEqual(ops.categories.length, 1); assert.strictEqual(ops.categories[0].metadata.label, 'uri1'); // unconfirmed! assert.strictEqual(ops.categories[0].metadata.label, 'uri1'); @@ -93,6 +98,7 @@ suite('BulkEditPreview', function () { const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); + store.add(ops); assert.strictEqual(ops.checked.isChecked(edits[0]), true); assert.strictEqual(ops.checked.isChecked(edits[1]), true); @@ -119,6 +125,7 @@ suite('BulkEditPreview', function () { ]; const ops = await instaService.invokeFunction(BulkFileOperations.create, edits); + store.add(ops); assert.strictEqual(ops.checked.isChecked(edits[0]), false); assert.strictEqual(ops.checked.isChecked(edits[1]), false); diff --git a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts index db9c3f69a74..f67df48598e 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatVariables.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; suite('ChatVariables', function () { @@ -15,10 +16,12 @@ suite('ChatVariables', function () { service = new ChatVariablesService(); }); + ensureNoDisposablesAreLeakedInTestSuite(); test('ChatVariables - resolveVariables', async function () { - service.registerVariable({ name: 'foo', description: 'bar' }, async () => ([{ level: 'full', value: 'farboo' }])); - service.registerVariable({ name: 'far', description: 'boo' }, async () => ([{ level: 'full', value: 'farboo' }])); + + const v1 = service.registerVariable({ name: 'foo', description: 'bar' }, async () => ([{ level: 'full', value: 'farboo' }])); + const v2 = service.registerVariable({ name: 'far', description: 'boo' }, async () => ([{ level: 'full', value: 'farboo' }])); { const data = await service.resolveVariables('Hello @foo and@far', null!, CancellationToken.None); @@ -59,5 +62,8 @@ suite('ChatVariables', function () { assert.deepEqual(Object.keys(data.variables).sort(), ['far', 'foo']); assert.strictEqual(data.prompt, 'Hello [@foo](values:foo) and [@far](values:far) [@foo](values:foo) @unknown'); } + + v1.dispose(); + v2.dispose(); }); }); diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index c1a086d323f..7013e373e2e 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -14,13 +14,13 @@ import { mock } from 'vs/base/test/common/mock'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('DecorationsService', function () { let service: DecorationsService; setup(function () { - service?.dispose(); service = new DecorationsService( new class extends mock() { override extUri = resources.extUri; @@ -29,6 +29,12 @@ suite('DecorationsService', function () { ); }); + teardown(function () { + service.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('Async provider, async/evented result', function () { return runWithFakedTimers({}, async function () { diff --git a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts index fdda3787190..47eb7efceba 100644 --- a/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/breadcrumbModel.test.ts @@ -13,9 +13,11 @@ import { TestContextService } from 'vs/workbench/test/common/workbenchTestServic import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { mock } from 'vs/base/test/common/mock'; import { IOutlineService } from 'vs/workbench/services/outline/browser/outline'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Breadcrumb Model', function () { + let model: BreadcrumbsModel; const workspaceService = new TestContextService(new Workspace('ffff', [new WorkspaceFolder({ uri: URI.parse('foo:/bar/baz/ws'), name: 'ws', index: 0 })])); const configService = new class extends TestConfigurationService { override getValue(...args: any[]) { @@ -32,9 +34,15 @@ suite('Breadcrumb Model', function () { } }; + teardown(function () { + model.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('only uri, inside workspace', function () { - const model = new BreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, configService, workspaceService, new class extends mock() { }); + model = new BreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/path/file.ts'), undefined, configService, workspaceService, new class extends mock() { }); const elements = model.getElements(); assert.strictEqual(elements.length, 3); @@ -49,7 +57,7 @@ suite('Breadcrumb Model', function () { test('display uri matters for FileElement', function () { - const model = new BreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/PATH/file.ts'), undefined, configService, workspaceService, new class extends mock() { }); + model = new BreadcrumbsModel(URI.parse('foo:/bar/baz/ws/some/PATH/file.ts'), undefined, configService, workspaceService, new class extends mock() { }); const elements = model.getElements(); assert.strictEqual(elements.length, 3); @@ -64,7 +72,7 @@ suite('Breadcrumb Model', function () { test('only uri, outside workspace', function () { - const model = new BreadcrumbsModel(URI.parse('foo:/outside/file.ts'), undefined, configService, workspaceService, new class extends mock() { }); + model = new BreadcrumbsModel(URI.parse('foo:/outside/file.ts'), undefined, configService, workspaceService, new class extends mock() { }); const elements = model.getElements(); assert.strictEqual(elements.length, 2); From 1c285ac806e9489d964ed08f8c700f8dddbc037d Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 6 Sep 2023 11:42:57 +0200 Subject: [PATCH 550/607] more `ensureNoDisposablesAreLeakedInTestSuite` work --- .../api/test/browser/mainThreadBulkEdits.test.ts | 5 +++++ .../api/test/browser/mainThreadCommands.test.ts | 8 ++++++++ .../api/test/browser/mainThreadDiagnostics.test.ts | 11 +++++++++++ .../mainThreadDocumentContentProviders.test.ts | 10 ++++++++-- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts b/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts index 3182972950e..959705f233c 100644 --- a/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts @@ -12,9 +12,12 @@ import { FileSystemProviderCapabilities, IFileService } from 'vs/platform/files/ import { reviveWorkspaceEditDto } from 'vs/workbench/api/browser/mainThreadBulkEdits'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { IWorkspaceTextEdit } from 'vs/editor/common/languages'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('MainThreadBulkEdits', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + test('"Rename failed to apply edits" in monorepo with pnpm #158845', function () { @@ -52,5 +55,7 @@ suite('MainThreadBulkEdits', function () { assert.strictEqual((out.edits[2]).resource.path, '/other/path.txt'); assert.strictEqual((out.edits[3]).resource.path, '/other/path.txt'); + uriIdentityService.dispose(); + }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts b/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts index fe6133d11ad..0c5d47544d1 100644 --- a/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadCommands.test.ts @@ -9,9 +9,12 @@ import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/c import { SingleProxyRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('MainThreadCommands', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + test('dispose on unregister', function () { const commands = new MainThreadCommands(SingleProxyRPCProtocol(null), undefined!, new class extends mock() { }); @@ -24,6 +27,9 @@ suite('MainThreadCommands', function () { // unregister commands.$unregisterCommand('foo'); assert.strictEqual(CommandsRegistry.getCommand('foo'), undefined); + + commands.dispose(); + }); test('unregister all on dispose', function () { @@ -83,5 +89,7 @@ suite('MainThreadCommands', function () { runs.length = 0; await commands.$executeCommand('bazz', [1, 2, true], false); assert.deepStrictEqual(runs, ['bazz']); + + commands.dispose(); }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadDiagnostics.test.ts b/src/vs/workbench/api/test/browser/mainThreadDiagnostics.test.ts index 9bd2eff631d..bf67789ff10 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDiagnostics.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDiagnostics.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { timeout } from 'vs/base/common/async'; import { URI, UriComponents } from 'vs/base/common/uri'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { MarkerService } from 'vs/platform/markers/common/markerService'; import { IMarkerData } from 'vs/platform/markers/common/markers'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -24,6 +25,12 @@ suite('MainThreadDiagnostics', function () { markerService = new MarkerService(); }); + teardown(function () { + markerService.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + test('clear markers on dispose', function () { const diag = new MainThreadDiagnostics( @@ -111,6 +118,8 @@ suite('MainThreadDiagnostics', function () { assert.strictEqual(changedData.length, 1); assert.strictEqual(changedData[0].length, 1); assert.strictEqual(changedData[0][0][1][0].message, 'forgein_owner'); + + diag.dispose(); }); }); @@ -158,6 +167,8 @@ suite('MainThreadDiagnostics', function () { await timeout(0); assert.strictEqual(markerService.read().length, 0); assert.strictEqual(changedData.length, 1); + + diag.dispose(); }); }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts index a1619c52ec4..8eaa58798d7 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts @@ -12,9 +12,12 @@ import { IModelService } from 'vs/editor/common/services/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { TestRPCProtocol } from 'vs/workbench/api/test/common/testRPCProtocol'; import { TextEdit } from 'vs/editor/common/languages'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('MainThreadDocumentContentProviders', function () { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + test('events are processed properly', function () { const uri = URI.parse('test:uri'); @@ -35,9 +38,12 @@ suite('MainThreadDocumentContentProviders', function () { }, ); + store.add(model); + store.add(providers); + return new Promise((resolve, reject) => { let expectedEvents = 1; - model.onDidChangeContent(e => { + store.add(model.onDidChangeContent(e => { expectedEvents -= 1; try { assert.ok(expectedEvents >= 0); @@ -48,7 +54,7 @@ suite('MainThreadDocumentContentProviders', function () { model.dispose(); resolve(); } - }); + })); providers.$onVirtualDocumentChange(uri, '1\n2'); providers.$onVirtualDocumentChange(uri, '1\n2\n3'); }); From 107eed53305994cf250cbe5d754e0eb01f7ba495 Mon Sep 17 00:00:00 2001 From: Johannes Date: Wed, 6 Sep 2023 12:11:52 +0200 Subject: [PATCH 551/607] fix an actual leaking CTS --- .../decorations/browser/decorationsService.ts | 10 ++++-- .../test/browser/decorationsService.test.ts | 35 +++++++++++-------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 44aaa12b056..e725eccdd1b 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -268,6 +268,7 @@ export class DecorationsService implements IDecorationsService { dispose(): void { this._onDidChangeDecorations.dispose(); this._onDidChangeDecorationsDelayed.dispose(); + this._data.clear(); } registerDecorationsProvider(provider: IDecorationsProvider): IDisposable { @@ -374,15 +375,16 @@ export class DecorationsService implements IDecorationsService { map.delete(provider); } - const source = new CancellationTokenSource(); - const dataOrThenable = provider.provideDecorations(uri, source.token); + const cts = new CancellationTokenSource(); + const dataOrThenable = provider.provideDecorations(uri, cts.token); if (!isThenable | undefined>(dataOrThenable)) { // sync -> we have a result now + cts.dispose(); return this._keepItem(map, provider, uri, dataOrThenable); } else { // async -> we have a result soon - const request = new DecorationDataRequest(source, Promise.resolve(dataOrThenable).then(data => { + const request = new DecorationDataRequest(cts, Promise.resolve(dataOrThenable).then(data => { if (map.get(provider) === request) { this._keepItem(map, provider, uri, data); } @@ -390,6 +392,8 @@ export class DecorationsService implements IDecorationsService { if (!isCancellationError(err) && map.get(provider) === request) { map.delete(provider); } + }).finally(() => { + cts.dispose(); })); map.set(provider, request); diff --git a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts index 7013e373e2e..ae94c92179d 100644 --- a/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts +++ b/src/vs/workbench/services/decorations/test/browser/decorationsService.test.ts @@ -33,7 +33,8 @@ suite('DecorationsService', function () { service.dispose(); }); - ensureNoDisposablesAreLeakedInTestSuite(); + const store = ensureNoDisposablesAreLeakedInTestSuite(); + test('Async provider, async/evented result', function () { @@ -42,7 +43,7 @@ suite('DecorationsService', function () { const uri = URI.parse('foo:bar'); let callCounter = 0; - service.registerDecorationsProvider(new class implements IDecorationsProvider { + const reg = service.registerDecorationsProvider(new class implements IDecorationsProvider { readonly label: string = 'Test'; readonly onDidChange: Event = Event.None; provideDecorations(uri: URI) { @@ -68,6 +69,8 @@ suite('DecorationsService', function () { assert.deepStrictEqual(service.getDecoration(uri, false)!.tooltip, 'T'); assert.deepStrictEqual(service.getDecoration(uri, false)!.strikethrough, true); assert.strictEqual(callCounter, 1); + + reg.dispose(); }); }); @@ -76,7 +79,7 @@ suite('DecorationsService', function () { const uri = URI.parse('foo:bar'); let callCounter = 0; - service.registerDecorationsProvider(new class implements IDecorationsProvider { + const reg = service.registerDecorationsProvider(new class implements IDecorationsProvider { readonly label: string = 'Test'; readonly onDidChange: Event = Event.None; provideDecorations(uri: URI) { @@ -89,6 +92,8 @@ suite('DecorationsService', function () { assert.deepStrictEqual(service.getDecoration(uri, false)!.tooltip, 'Z'); assert.deepStrictEqual(service.getDecoration(uri, false)!.strikethrough, false); assert.strictEqual(callCounter, 1); + + reg.dispose(); }); test('Clear decorations on provider dispose', async function () { @@ -113,11 +118,12 @@ suite('DecorationsService', function () { // un-register -> ensure good event let didSeeEvent = false; const p = new Promise(resolve => { - service.onDidChangeDecorations(e => { + const l = service.onDidChangeDecorations(e => { assert.strictEqual(e.affectsResource(uri), true); assert.deepStrictEqual(service.getDecoration(uri, false), undefined); assert.strictEqual(callCounter, 1); didSeeEvent = true; + l.dispose(); resolve(); }); }); @@ -165,12 +171,12 @@ suite('DecorationsService', function () { deco = service.getDecoration(childUri.with({ path: 'some/path/' }), true)!; assert.strictEqual(typeof deco.tooltip, 'string'); + reg.dispose(); }); test('Decorations not showing up for second root folder #48502', async function () { let cancelCount = 0; - const winjsCancelCount = 0; let callCount = 0; const provider = new class implements IDecorationsProvider { @@ -182,9 +188,9 @@ suite('DecorationsService', function () { provideDecorations(uri: URI, token: CancellationToken): Promise { - token.onCancellationRequested(() => { + store.add(token.onCancellationRequested(() => { cancelCount += 1; - }); + })); return new Promise(resolve => { callCount += 1; @@ -198,15 +204,16 @@ suite('DecorationsService', function () { const reg = service.registerDecorationsProvider(provider); const uri = URI.parse('foo://bar'); - service.getDecoration(uri, false); + const d1 = service.getDecoration(uri, false); provider._onDidChange.fire([uri]); - service.getDecoration(uri, false); + const d2 = service.getDecoration(uri, false); assert.strictEqual(cancelCount, 1); - assert.strictEqual(winjsCancelCount, 0); assert.strictEqual(callCount, 2); + d1?.dispose(); + d2?.dispose(); reg.dispose(); }); @@ -320,23 +327,23 @@ suite('DecorationsService', function () { const invokeOrder: string[] = []; - service.registerDecorationsProvider(new class { + store.add(service.registerDecorationsProvider(new class { label = 'Provider-1'; onDidChange = Event.None; provideDecorations() { invokeOrder.push(this.label); return undefined; } - }); + })); - service.registerDecorationsProvider(new class { + store.add(service.registerDecorationsProvider(new class { label = 'Provider-2'; onDidChange = Event.None; provideDecorations() { invokeOrder.push(this.label); return undefined; } - }); + })); service.getDecoration(URI.parse('test://me/path'), false); From 1f40d64450a477bebf96c099841ef30c7c5098f6 Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 6 Sep 2023 21:24:39 +0900 Subject: [PATCH 552/607] fix: deb and rpm dependency after keytar removal (#192275) * fix: deb and rpm dependency after keytar removal * chore: rm libsecret-1-dev from CI * ci: update cache --- .github/workflows/ci.yml | 2 +- build/.cachesalt | 2 +- build/azure-pipelines/linux/product-build-linux.yml | 1 - build/azure-pipelines/product-compile.yml | 2 +- build/linux/debian/dep-lists.js | 8 +------- build/linux/debian/dep-lists.ts | 6 ------ build/linux/rpm/dep-lists.js | 5 +---- build/linux/rpm/dep-lists.ts | 3 --- 8 files changed, 5 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d49c5b204f..bf63d22e313 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: - name: Setup Build Environment run: | sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libkrb5-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 + sudo apt-get install -y libxkbfile-dev pkg-config libkrb5-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 diff --git a/build/.cachesalt b/build/.cachesalt index 26ad5de2bca..1aad2ab5206 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2023-07-20T13:31:34.746Z +2023-09-06T09:54:45.225Z diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 919d48eac37..734292ad4a3 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -52,7 +52,6 @@ steps: libgtk-3-0 \ libgbm1 \ libxkbfile-dev \ - libsecret-1-dev \ libkrb5-dev sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb sudo chmod +x /etc/init.d/xvfb diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index 4bcf3fd9114..28cecdd87cd 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -52,7 +52,7 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libsecret-1-dev libnotify-bin libkrb5-dev + - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libnotify-bin libkrb5-dev displayName: Install build tools condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) diff --git a/build/linux/debian/dep-lists.js b/build/linux/debian/dep-lists.js index bd2ce4fa3d7..e60b340b45e 100644 --- a/build/linux/debian/dep-lists.js +++ b/build/linux/debian/dep-lists.js @@ -39,7 +39,6 @@ exports.referenceGeneratedDepsByArch = { 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', - 'libglib2.0-0 (>= 2.16.0)', 'libglib2.0-0 (>= 2.37.3)', 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', @@ -49,7 +48,6 @@ exports.referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', - 'libsecret-1-0 (>= 0.18)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', @@ -80,7 +78,6 @@ exports.referenceGeneratedDepsByArch = { 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', - 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.37.3)', 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', @@ -90,7 +87,6 @@ exports.referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', - 'libsecret-1-0 (>= 0.18)', 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', @@ -119,7 +115,6 @@ exports.referenceGeneratedDepsByArch = { 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', - 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.37.3)', 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', @@ -129,7 +124,6 @@ exports.referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', - 'libsecret-1-0 (>= 0.18)', 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', @@ -146,4 +140,4 @@ exports.referenceGeneratedDepsByArch = { 'xdg-utils (>= 1.0.2)' ] }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQjtJQUN0QixrQkFBa0I7SUFDbEIsV0FBVztDQUNYLENBQUM7QUFFRixvSEFBb0g7QUFDcEgsMENBQTBDO0FBQzFDLDhEQUE4RDtBQUNqRCxRQUFBLGVBQWUsR0FBRztJQUM5QixZQUFZLENBQUMseUVBQXlFO0NBQ3RGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHO0lBQzNDLE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQiwwQkFBMEI7UUFDMUIsa0JBQWtCO1FBQ2xCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsV0FBVztRQUNYLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1Qix5QkFBeUI7UUFDekIsVUFBVTtRQUNWLDBCQUEwQjtRQUMxQixvQkFBb0I7UUFDcEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QixVQUFVO1FBQ1YsWUFBWTtRQUNaLDBCQUEwQjtRQUMxQixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtLQUN0QjtJQUNELE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsc0JBQXNCO1FBQ3RCLHNEQUFzRDtRQUN0RCx5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLHNCQUFzQjtRQUN0Qix5QkFBeUI7UUFDekIsMEJBQTBCO1FBQzFCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0lBQ0QsT0FBTyxFQUFFO1FBQ1IsaUJBQWlCO1FBQ2pCLHdCQUF3QjtRQUN4QiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLDJCQUEyQjtRQUMzQixpQkFBaUI7UUFDakIsc0JBQXNCO1FBQ3RCLHNEQUFzRDtRQUN0RCx3QkFBd0I7UUFDeEIscUJBQXFCO1FBQ3JCLHNCQUFzQjtRQUN0Qix5QkFBeUI7UUFDekIsMEJBQTBCO1FBQzFCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLHlCQUF5QjtRQUN6QixtQkFBbUI7UUFDbkIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0NBQ0QsQ0FBQyJ9 \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLGtIQUFrSDtBQUNsSCw0REFBNEQ7QUFDL0MsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHFDQUFxQztJQUNyQyxtQkFBbUI7SUFDbkIsc0RBQXNEO0lBQ3RELHNCQUFzQjtJQUN0QixrQkFBa0I7SUFDbEIsV0FBVztDQUNYLENBQUM7QUFFRixvSEFBb0g7QUFDcEgsMENBQTBDO0FBQzFDLDhEQUE4RDtBQUNqRCxRQUFBLGVBQWUsR0FBRztJQUM5QixZQUFZLENBQUMseUVBQXlFO0NBQ3RGLENBQUM7QUFFVyxRQUFBLDRCQUE0QixHQUFHO0lBQzNDLE9BQU8sRUFBRTtRQUNSLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLHdCQUF3QjtRQUN4QiwyQkFBMkI7UUFDM0IsaUJBQWlCO1FBQ2pCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixpQkFBaUI7UUFDakIsaUJBQWlCO1FBQ2pCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsZ0JBQWdCO1FBQ2hCLHNCQUFzQjtRQUN0QixzREFBc0Q7UUFDdEQseUJBQXlCO1FBQ3pCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIseUJBQXlCO1FBQ3pCLDBCQUEwQjtRQUMxQixrQkFBa0I7UUFDbEIsd0JBQXdCO1FBQ3hCLHFDQUFxQztRQUNyQyxXQUFXO1FBQ1gsd0JBQXdCO1FBQ3hCLHFCQUFxQjtRQUNyQixtQkFBbUI7UUFDbkIsNEJBQTRCO1FBQzVCLG1CQUFtQjtRQUNuQixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLFVBQVU7UUFDViwwQkFBMEI7UUFDMUIsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsVUFBVTtRQUNWLFlBQVk7UUFDWiwwQkFBMEI7UUFDMUIsYUFBYTtRQUNiLFlBQVk7UUFDWixzQkFBc0I7S0FDdEI7SUFDRCxPQUFPLEVBQUU7UUFDUixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLCtCQUErQjtRQUMvQix3QkFBd0I7UUFDeEIsMkJBQTJCO1FBQzNCLGlCQUFpQjtRQUNqQixzQkFBc0I7UUFDdEIsc0RBQXNEO1FBQ3RELHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLHlCQUF5QjtRQUN6QiwwQkFBMEI7UUFDMUIsa0JBQWtCO1FBQ2xCLHdCQUF3QjtRQUN4QixxQ0FBcUM7UUFDckMsV0FBVztRQUNYLHdCQUF3QjtRQUN4QixxQkFBcUI7UUFDckIsbUJBQW1CO1FBQ25CLDRCQUE0QjtRQUM1QixtQkFBbUI7UUFDbkIscUJBQXFCO1FBQ3JCLG1CQUFtQjtRQUNuQixVQUFVO1FBQ1YsMEJBQTBCO1FBQzFCLG9CQUFvQjtRQUNwQiwrQkFBK0I7UUFDL0Isd0JBQXdCO1FBQ3hCLFVBQVU7UUFDVixZQUFZO1FBQ1osMEJBQTBCO1FBQzFCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO0tBQ3RCO0NBQ0QsQ0FBQyJ9 \ No newline at end of file diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts index 842eaac27cc..c430f3b5ec3 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts @@ -39,7 +39,6 @@ export const referenceGeneratedDepsByArch = { 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', - 'libglib2.0-0 (>= 2.16.0)', 'libglib2.0-0 (>= 2.37.3)', 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', @@ -49,7 +48,6 @@ export const referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', - 'libsecret-1-0 (>= 0.18)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', @@ -80,7 +78,6 @@ export const referenceGeneratedDepsByArch = { 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', - 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.37.3)', 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', @@ -90,7 +87,6 @@ export const referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', - 'libsecret-1-0 (>= 0.18)', 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', @@ -119,7 +115,6 @@ export const referenceGeneratedDepsByArch = { 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.0.1)', 'libgbm1 (>= 17.1.0~rc2)', - 'libglib2.0-0 (>= 2.12.0)', 'libglib2.0-0 (>= 2.37.3)', 'libgssapi-krb5-2', 'libgtk-3-0 (>= 3.9.10)', @@ -129,7 +124,6 @@ export const referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', - 'libsecret-1-0 (>= 0.18)', 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index 5e2749a50be..667d207ca9d 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -96,7 +96,6 @@ exports.referenceGeneratedDepsByArch = { 'libpthread.so.0(GLIBC_2.3.4)(64bit)', 'librt.so.1()(64bit)', 'librt.so.1(GLIBC_2.2.5)(64bit)', - 'libsecret-1.so.0()(64bit)', 'libsmime3.so()(64bit)', 'libsmime3.so(NSS_3.10)(64bit)', 'libsmime3.so(NSS_3.2)(64bit)', @@ -181,7 +180,6 @@ exports.referenceGeneratedDepsByArch = { 'libpthread.so.0(GLIBC_2.4)', 'librt.so.1', 'librt.so.1(GLIBC_2.4)', - 'libsecret-1.so.0', 'libsmime3.so', 'libsmime3.so(NSS_3.10)', 'libsmime3.so(NSS_3.2)', @@ -273,7 +271,6 @@ exports.referenceGeneratedDepsByArch = { 'libpthread.so.0(GLIBC_2.17)(64bit)', 'librt.so.1()(64bit)', 'librt.so.1(GLIBC_2.17)(64bit)', - 'libsecret-1.so.0()(64bit)', 'libsmime3.so()(64bit)', 'libsmime3.so(NSS_3.10)(64bit)', 'libsmime3.so(NSS_3.2)(64bit)', @@ -305,4 +302,4 @@ exports.referenceGeneratedDepsByArch = { 'xdg-utils' ] }; -//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLCtHQUErRztBQUMvRywrREFBK0Q7QUFDbEQsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHdCQUF3QjtJQUN4Qiw2QkFBNkI7SUFDN0IsNkJBQTZCO0lBQzdCLGdDQUFnQztJQUNoQyx5QkFBeUI7SUFDekIsdUJBQXVCO0lBQ3ZCLFdBQVcsQ0FBQyxpQkFBaUI7Q0FDN0IsQ0FBQztBQUVXLFFBQUEsNEJBQTRCLEdBQUc7SUFDM0MsUUFBUSxFQUFFO1FBQ1QsaUJBQWlCO1FBQ2pCLCtCQUErQjtRQUMvQiwwQ0FBMEM7UUFDMUMsd0NBQXdDO1FBQ3hDLHNCQUFzQjtRQUN0Qiw2QkFBNkI7UUFDN0IsMEJBQTBCO1FBQzFCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6QixpQ0FBaUM7UUFDakMsc0NBQXNDO1FBQ3RDLDBCQUEwQjtRQUMxQixpQ0FBaUM7UUFDakMsd0JBQXdCO1FBQ3hCLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQkFBK0I7UUFDL0IsNkJBQTZCO1FBQzdCLCtCQUErQjtRQUMvQiwrQkFBK0I7UUFDL0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0Isd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLDBCQUEwQjtRQUMxQiwyQkFBMkI7UUFDM0IsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQ0FBK0M7UUFDL0Msd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2QixpQ0FBaUM7UUFDakMsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQixzQkFBc0I7UUFDdEIscUJBQXFCO1FBQ3JCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsNkJBQTZCO1FBQzdCLDRCQUE0QjtRQUM1Qiw2QkFBNkI7UUFDN0IsNEJBQTRCO1FBQzVCLDRCQUE0QjtRQUM1Qiw4QkFBOEI7UUFDOUIseUJBQXlCO1FBQ3pCLHVDQUF1QztRQUN2Qyw0QkFBNEI7UUFDNUIsMEJBQTBCO1FBQzFCLG9DQUFvQztRQUNwQyxxQ0FBcUM7UUFDckMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxxQ0FBcUM7UUFDckMscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQywyQkFBMkI7UUFDM0IsdUJBQXVCO1FBQ3ZCLCtCQUErQjtRQUMvQiw4QkFBOEI7UUFDOUIsNkJBQTZCO1FBQzdCLHVCQUF1QjtRQUN2QixrQ0FBa0M7UUFDbEMsc0JBQXNCO1FBQ3RCLDRCQUE0QjtRQUM1QiwwQkFBMEI7UUFDMUIsZ0NBQWdDO1FBQ2hDLGdCQUFnQjtRQUNoQixXQUFXO0tBQ1g7SUFDRCxTQUFTLEVBQUU7UUFDVixpQkFBaUI7UUFDakIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxhQUFhO1FBQ2Isb0JBQW9CO1FBQ3BCLGlCQUFpQjtRQUNqQixjQUFjO1FBQ2QsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQixnQkFBZ0I7UUFDaEIsMEJBQTBCO1FBQzFCLCtCQUErQjtRQUMvQixpQkFBaUI7UUFDakIsd0JBQXdCO1FBQ3hCLGVBQWU7UUFDZixXQUFXO1FBQ1gsdUJBQXVCO1FBQ3ZCLHVCQUF1QjtRQUN2Qix1QkFBdUI7UUFDdkIsdUJBQXVCO1FBQ3ZCLHVCQUF1QjtRQUN2QixzQkFBc0I7UUFDdEIsc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0QixzQkFBc0I7UUFDdEIsc0JBQXNCO1FBQ3RCLGVBQWU7UUFDZix1QkFBdUI7UUFDdkIsZ0JBQWdCO1FBQ2hCLFlBQVk7UUFDWix1QkFBdUI7UUFDdkIsYUFBYTtRQUNiLGVBQWU7UUFDZixhQUFhO1FBQ2IsZUFBZTtRQUNmLHdCQUF3QjtRQUN4Qix3QkFBd0I7UUFDeEIsaUJBQWlCO1FBQ2pCLGtCQUFrQjtRQUNsQixxQkFBcUI7UUFDckIscUJBQXFCO1FBQ3JCLHdDQUF3QztRQUN4QyxlQUFlO1FBQ2Ysd0JBQXdCO1FBQ3hCLGNBQWM7UUFDZCwwQkFBMEI7UUFDMUIsV0FBVztRQUNYLHNCQUFzQjtRQUN0QixhQUFhO1FBQ2IsWUFBWTtRQUNaLHNCQUFzQjtRQUN0QixzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLDZCQUE2QjtRQUM3QixxQkFBcUI7UUFDckIsc0JBQXNCO1FBQ3RCLHFCQUFxQjtRQUNyQixxQkFBcUI7UUFDckIsdUJBQXVCO1FBQ3ZCLGdCQUFnQjtRQUNoQixnQ0FBZ0M7UUFDaEMsbUJBQW1CO1FBQ25CLGlCQUFpQjtRQUNqQiw2QkFBNkI7UUFDN0IsNEJBQTRCO1FBQzVCLFlBQVk7UUFDWix1QkFBdUI7UUFDdkIsa0JBQWtCO1FBQ2xCLGNBQWM7UUFDZCx3QkFBd0I7UUFDeEIsdUJBQXVCO1FBQ3ZCLDZCQUE2QjtRQUM3QixnQkFBZ0I7UUFDaEIsNEJBQTRCO1FBQzVCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLGtDQUFrQztRQUNsQyw2QkFBNkI7UUFDN0IsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQywrQkFBK0I7UUFDL0IsK0JBQStCO1FBQy9CLGNBQWM7UUFDZCx5QkFBeUI7UUFDekIsYUFBYTtRQUNiLG1CQUFtQjtRQUNuQixpQkFBaUI7UUFDakIsZ0NBQWdDO1FBQ2hDLGdCQUFnQjtRQUNoQixXQUFXO0tBQ1g7SUFDRCxTQUFTLEVBQUU7UUFDVixpQkFBaUI7UUFDakIsZ0NBQWdDO1FBQ2hDLDBDQUEwQztRQUMxQyxzQkFBc0I7UUFDdEIsNkJBQTZCO1FBQzdCLDBCQUEwQjtRQUMxQix1QkFBdUI7UUFDdkIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6Qix5QkFBeUI7UUFDekIsaUNBQWlDO1FBQ2pDLHNDQUFzQztRQUN0QywwQkFBMEI7UUFDMUIsaUNBQWlDO1FBQ2pDLHdCQUF3QjtRQUN4QixvQkFBb0I7UUFDcEIsOEJBQThCO1FBQzlCLHdCQUF3QjtRQUN4Qix1QkFBdUI7UUFDdkIseUJBQXlCO1FBQ3pCLG9DQUFvQztRQUNwQyxxQkFBcUI7UUFDckIsK0JBQStCO1FBQy9CLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsc0JBQXNCO1FBQ3RCLHdCQUF3QjtRQUN4QiwrQkFBK0I7UUFDL0IsaUNBQWlDO1FBQ2pDLGlDQUFpQztRQUNqQywwQkFBMEI7UUFDMUIsMkJBQTJCO1FBQzNCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsK0NBQStDO1FBQy9DLHdCQUF3QjtRQUN4Qix1QkFBdUI7UUFDdkIsaUNBQWlDO1FBQ2pDLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsc0JBQXNCO1FBQ3RCLHFCQUFxQjtRQUNyQiw2QkFBNkI7UUFDN0IsNkJBQTZCO1FBQzdCLCtCQUErQjtRQUMvQiw2QkFBNkI7UUFDN0IsNEJBQTRCO1FBQzVCLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsNkJBQTZCO1FBQzdCLDRCQUE0QjtRQUM1Qiw0QkFBNEI7UUFDNUIsOEJBQThCO1FBQzlCLHlCQUF5QjtRQUN6Qix1Q0FBdUM7UUFDdkMsNEJBQTRCO1FBQzVCLDBCQUEwQjtRQUMxQixvQ0FBb0M7UUFDcEMscUJBQXFCO1FBQ3JCLCtCQUErQjtRQUMvQiwyQkFBMkI7UUFDM0IsdUJBQXVCO1FBQ3ZCLCtCQUErQjtRQUMvQiw4QkFBOEI7UUFDOUIsNkJBQTZCO1FBQzdCLHlCQUF5QjtRQUN6QixtQ0FBbUM7UUFDbkMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxxQ0FBcUM7UUFDckMsb0NBQW9DO1FBQ3BDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsc0NBQXNDO1FBQ3RDLHNDQUFzQztRQUN0Qyx1QkFBdUI7UUFDdkIsaUNBQWlDO1FBQ2pDLHNCQUFzQjtRQUN0Qiw0QkFBNEI7UUFDNUIsbUNBQW1DO1FBQ25DLDBCQUEwQjtRQUMxQixnQ0FBZ0M7UUFDaEMsZ0JBQWdCO1FBQ2hCLFdBQVc7S0FDWDtDQUNELENBQUMifQ== \ No newline at end of file +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGVwLWxpc3RzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiZGVwLWxpc3RzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQTs7O2dHQUdnRzs7O0FBRWhHLCtHQUErRztBQUMvRywrREFBK0Q7QUFDbEQsUUFBQSxjQUFjLEdBQUc7SUFDN0IsaUJBQWlCO0lBQ2pCLHdCQUF3QjtJQUN4Qiw2QkFBNkI7SUFDN0IsNkJBQTZCO0lBQzdCLGdDQUFnQztJQUNoQyx5QkFBeUI7SUFDekIsdUJBQXVCO0lBQ3ZCLFdBQVcsQ0FBQyxpQkFBaUI7Q0FDN0IsQ0FBQztBQUVXLFFBQUEsNEJBQTRCLEdBQUc7SUFDM0MsUUFBUSxFQUFFO1FBQ1QsaUJBQWlCO1FBQ2pCLCtCQUErQjtRQUMvQiwwQ0FBMEM7UUFDMUMsd0NBQXdDO1FBQ3hDLHNCQUFzQjtRQUN0Qiw2QkFBNkI7UUFDN0IsMEJBQTBCO1FBQzFCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLHlCQUF5QjtRQUN6QixpQ0FBaUM7UUFDakMsc0NBQXNDO1FBQ3RDLDBCQUEwQjtRQUMxQixpQ0FBaUM7UUFDakMsd0JBQXdCO1FBQ3hCLG9CQUFvQjtRQUNwQiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQkFBK0I7UUFDL0IsNkJBQTZCO1FBQzdCLCtCQUErQjtRQUMvQiwrQkFBK0I7UUFDL0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0Isd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qix5QkFBeUI7UUFDekIscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyxzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLDBCQUEwQjtRQUMxQiwyQkFBMkI7UUFDM0IsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QiwrQ0FBK0M7UUFDL0Msd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2QixpQ0FBaUM7UUFDakMsb0JBQW9CO1FBQ3BCLCtCQUErQjtRQUMvQixzQkFBc0I7UUFDdEIscUJBQXFCO1FBQ3JCLDZCQUE2QjtRQUM3Qiw2QkFBNkI7UUFDN0IsK0JBQStCO1FBQy9CLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsNkJBQTZCO1FBQzdCLDRCQUE0QjtRQUM1Qiw2QkFBNkI7UUFDN0IsNEJBQTRCO1FBQzVCLDRCQUE0QjtRQUM1Qiw4QkFBOEI7UUFDOUIseUJBQXlCO1FBQ3pCLHVDQUF1QztRQUN2Qyw0QkFBNEI7UUFDNUIsMEJBQTBCO1FBQzFCLG9DQUFvQztRQUNwQyxxQ0FBcUM7UUFDckMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxxQ0FBcUM7UUFDckMscUJBQXFCO1FBQ3JCLGdDQUFnQztRQUNoQyx1QkFBdUI7UUFDdkIsK0JBQStCO1FBQy9CLDhCQUE4QjtRQUM5Qiw2QkFBNkI7UUFDN0IsdUJBQXVCO1FBQ3ZCLGtDQUFrQztRQUNsQyxzQkFBc0I7UUFDdEIsNEJBQTRCO1FBQzVCLDBCQUEwQjtRQUMxQixnQ0FBZ0M7UUFDaEMsZ0JBQWdCO1FBQ2hCLFdBQVc7S0FDWDtJQUNELFNBQVMsRUFBRTtRQUNWLGlCQUFpQjtRQUNqQixxQkFBcUI7UUFDckIsZ0NBQWdDO1FBQ2hDLGFBQWE7UUFDYixvQkFBb0I7UUFDcEIsaUJBQWlCO1FBQ2pCLGNBQWM7UUFDZCxnQkFBZ0I7UUFDaEIsZ0JBQWdCO1FBQ2hCLGdCQUFnQjtRQUNoQiwwQkFBMEI7UUFDMUIsK0JBQStCO1FBQy9CLGlCQUFpQjtRQUNqQix3QkFBd0I7UUFDeEIsZUFBZTtRQUNmLFdBQVc7UUFDWCx1QkFBdUI7UUFDdkIsdUJBQXVCO1FBQ3ZCLHVCQUF1QjtRQUN2Qix1QkFBdUI7UUFDdkIsdUJBQXVCO1FBQ3ZCLHNCQUFzQjtRQUN0QixzQkFBc0I7UUFDdEIsc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0QixzQkFBc0I7UUFDdEIsZUFBZTtRQUNmLHVCQUF1QjtRQUN2QixnQkFBZ0I7UUFDaEIsWUFBWTtRQUNaLHVCQUF1QjtRQUN2QixhQUFhO1FBQ2IsZUFBZTtRQUNmLGFBQWE7UUFDYixlQUFlO1FBQ2Ysd0JBQXdCO1FBQ3hCLHdCQUF3QjtRQUN4QixpQkFBaUI7UUFDakIsa0JBQWtCO1FBQ2xCLHFCQUFxQjtRQUNyQixxQkFBcUI7UUFDckIsd0NBQXdDO1FBQ3hDLGVBQWU7UUFDZix3QkFBd0I7UUFDeEIsY0FBYztRQUNkLDBCQUEwQjtRQUMxQixXQUFXO1FBQ1gsc0JBQXNCO1FBQ3RCLGFBQWE7UUFDYixZQUFZO1FBQ1osc0JBQXNCO1FBQ3RCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsc0JBQXNCO1FBQ3RCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIsNkJBQTZCO1FBQzdCLHFCQUFxQjtRQUNyQixzQkFBc0I7UUFDdEIscUJBQXFCO1FBQ3JCLHFCQUFxQjtRQUNyQix1QkFBdUI7UUFDdkIsZ0JBQWdCO1FBQ2hCLGdDQUFnQztRQUNoQyxtQkFBbUI7UUFDbkIsaUJBQWlCO1FBQ2pCLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsWUFBWTtRQUNaLHVCQUF1QjtRQUN2QixjQUFjO1FBQ2Qsd0JBQXdCO1FBQ3hCLHVCQUF1QjtRQUN2Qiw2QkFBNkI7UUFDN0IsZ0JBQWdCO1FBQ2hCLDRCQUE0QjtRQUM1Qiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLDhCQUE4QjtRQUM5QixrQ0FBa0M7UUFDbEMsNkJBQTZCO1FBQzdCLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsZ0NBQWdDO1FBQ2hDLGdDQUFnQztRQUNoQyxnQ0FBZ0M7UUFDaEMsK0JBQStCO1FBQy9CLCtCQUErQjtRQUMvQixjQUFjO1FBQ2QseUJBQXlCO1FBQ3pCLGFBQWE7UUFDYixtQkFBbUI7UUFDbkIsaUJBQWlCO1FBQ2pCLGdDQUFnQztRQUNoQyxnQkFBZ0I7UUFDaEIsV0FBVztLQUNYO0lBQ0QsU0FBUyxFQUFFO1FBQ1YsaUJBQWlCO1FBQ2pCLGdDQUFnQztRQUNoQywwQ0FBMEM7UUFDMUMsc0JBQXNCO1FBQ3RCLDZCQUE2QjtRQUM3QiwwQkFBMEI7UUFDMUIsdUJBQXVCO1FBQ3ZCLHlCQUF5QjtRQUN6Qix5QkFBeUI7UUFDekIseUJBQXlCO1FBQ3pCLGlDQUFpQztRQUNqQyxzQ0FBc0M7UUFDdEMsMEJBQTBCO1FBQzFCLGlDQUFpQztRQUNqQyx3QkFBd0I7UUFDeEIsb0JBQW9CO1FBQ3BCLDhCQUE4QjtRQUM5Qix3QkFBd0I7UUFDeEIsdUJBQXVCO1FBQ3ZCLHlCQUF5QjtRQUN6QixvQ0FBb0M7UUFDcEMscUJBQXFCO1FBQ3JCLCtCQUErQjtRQUMvQixzQkFBc0I7UUFDdEIsd0JBQXdCO1FBQ3hCLHNCQUFzQjtRQUN0Qix3QkFBd0I7UUFDeEIsK0JBQStCO1FBQy9CLGlDQUFpQztRQUNqQyxpQ0FBaUM7UUFDakMsMEJBQTBCO1FBQzFCLDJCQUEyQjtRQUMzQiw4QkFBOEI7UUFDOUIsOEJBQThCO1FBQzlCLCtDQUErQztRQUMvQyx3QkFBd0I7UUFDeEIsdUJBQXVCO1FBQ3ZCLGlDQUFpQztRQUNqQyxvQkFBb0I7UUFDcEIsOEJBQThCO1FBQzlCLHNCQUFzQjtRQUN0QixxQkFBcUI7UUFDckIsNkJBQTZCO1FBQzdCLDZCQUE2QjtRQUM3QiwrQkFBK0I7UUFDL0IsNkJBQTZCO1FBQzdCLDRCQUE0QjtRQUM1Qiw2QkFBNkI7UUFDN0IsNEJBQTRCO1FBQzVCLDZCQUE2QjtRQUM3Qiw0QkFBNEI7UUFDNUIsNEJBQTRCO1FBQzVCLDhCQUE4QjtRQUM5Qix5QkFBeUI7UUFDekIsdUNBQXVDO1FBQ3ZDLDRCQUE0QjtRQUM1QiwwQkFBMEI7UUFDMUIsb0NBQW9DO1FBQ3BDLHFCQUFxQjtRQUNyQiwrQkFBK0I7UUFDL0IsdUJBQXVCO1FBQ3ZCLCtCQUErQjtRQUMvQiw4QkFBOEI7UUFDOUIsNkJBQTZCO1FBQzdCLHlCQUF5QjtRQUN6QixtQ0FBbUM7UUFDbkMscUNBQXFDO1FBQ3JDLHFDQUFxQztRQUNyQyxxQ0FBcUM7UUFDckMsb0NBQW9DO1FBQ3BDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsdUNBQXVDO1FBQ3ZDLHVDQUF1QztRQUN2Qyx1Q0FBdUM7UUFDdkMsc0NBQXNDO1FBQ3RDLHNDQUFzQztRQUN0Qyx1QkFBdUI7UUFDdkIsaUNBQWlDO1FBQ2pDLHNCQUFzQjtRQUN0Qiw0QkFBNEI7UUFDNUIsbUNBQW1DO1FBQ25DLDBCQUEwQjtRQUMxQixnQ0FBZ0M7UUFDaEMsZ0JBQWdCO1FBQ2hCLFdBQVc7S0FDWDtDQUNELENBQUMifQ== \ No newline at end of file diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 4e5f64dc20b..2a30ba9d640 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -95,7 +95,6 @@ export const referenceGeneratedDepsByArch = { 'libpthread.so.0(GLIBC_2.3.4)(64bit)', 'librt.so.1()(64bit)', 'librt.so.1(GLIBC_2.2.5)(64bit)', - 'libsecret-1.so.0()(64bit)', 'libsmime3.so()(64bit)', 'libsmime3.so(NSS_3.10)(64bit)', 'libsmime3.so(NSS_3.2)(64bit)', @@ -180,7 +179,6 @@ export const referenceGeneratedDepsByArch = { 'libpthread.so.0(GLIBC_2.4)', 'librt.so.1', 'librt.so.1(GLIBC_2.4)', - 'libsecret-1.so.0', 'libsmime3.so', 'libsmime3.so(NSS_3.10)', 'libsmime3.so(NSS_3.2)', @@ -272,7 +270,6 @@ export const referenceGeneratedDepsByArch = { 'libpthread.so.0(GLIBC_2.17)(64bit)', 'librt.so.1()(64bit)', 'librt.so.1(GLIBC_2.17)(64bit)', - 'libsecret-1.so.0()(64bit)', 'libsmime3.so()(64bit)', 'libsmime3.so(NSS_3.10)(64bit)', 'libsmime3.so(NSS_3.2)(64bit)', From c71e8d7bb5271b9da06b51ae0331bb1a4f5b96bd Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 6 Sep 2023 14:25:26 +0200 Subject: [PATCH 553/607] checking that view exists before running code --- .../contrib/stickyScroll/browser/stickyScrollWidget.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index f98f67e3570..9a47963427b 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -168,6 +168,9 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const layoutInfo = this._editor.getLayoutInfo(); for (const [index, line] of this._lineNumbers.entries()) { const renderedStickyLine = this._renderChildNode(index, line, layoutInfo, foldingModel); + if (!renderedStickyLine) { + continue; + } this._linesDomNode.appendChild(renderedStickyLine.lineDomNode); this._lineNumbersDomNode.appendChild(renderedStickyLine.lineNumberDomNode); this._stickyLines.push(renderedStickyLine); @@ -214,9 +217,12 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { })); } - private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null | undefined): RenderedStickyLine { + private _renderChildNode(index: number, line: number, layoutInfo: EditorLayoutInfo, foldingModel: FoldingModel | null | undefined): RenderedStickyLine | undefined { const viewModel = this._editor._getViewModel(); - const viewLineNumber = viewModel!.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; + if (!viewModel) { + return; + } + const viewLineNumber = viewModel.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); const minimapSide = this._editor.getOption(EditorOption.minimap).side; const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers); From a4e18708964cc82a1f8c4f2908935e0463c15acf Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 6 Sep 2023 14:35:55 +0200 Subject: [PATCH 554/607] Git - fix stage/unstage selected ranges in nested git repositories (#191987) * Git - fix stage/unstage selected ranges in nested git repositories * Remove the submodule check as it is being covered by the repository check * Pull request feedback --- extensions/git/src/model.ts | 6 +++--- extensions/git/src/repository.ts | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 02ff0ec8f5b..03a6ccf18ad 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -5,7 +5,7 @@ import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, commands, LogOutputChannel, l10n, ProgressLocation, WorkspaceFolder } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; -import { Repository, RepositoryState } from './repository'; +import { IRepositoryResolver, Repository, RepositoryState } from './repository'; import { memoize, sequentialize, debounce } from './decorators'; import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util'; import { Git } from './git'; @@ -170,7 +170,7 @@ class UnsafeRepositoriesManager { } } -export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry { +export class Model implements IRepositoryResolver, IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry { private _onDidOpenRepository = new EventEmitter(); readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; @@ -578,7 +578,7 @@ export class Model implements IBranchProtectionProviderRegistry, IRemoteSourcePu // Open repository const [dotGit, repositoryRootRealPath] = await Promise.all([this.git.getRepositoryDotGit(repositoryRoot), this.getRepositoryRootRealPath(repositoryRoot)]); - const repository = new Repository(this.git.open(repositoryRoot, repositoryRootRealPath, dotGit, this.logger), this, this, this, this, this.globalState, this.logger, this.telemetryReporter); + const repository = new Repository(this.git.open(repositoryRoot, repositoryRootRealPath, dotGit, this.logger), this, this, this, this, this, this.globalState, this.logger, this.telemetryReporter); this.open(repository); this._closedRepositoriesManager.deleteRepository(repository.root); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index c3092d37b63..ea4d6cbe56f 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -633,6 +633,14 @@ interface BranchProtectionMatcher { exclude?: picomatch.Matcher; } +export interface IRepositoryResolver { + getRepository(sourceControl: SourceControl): Repository | undefined; + getRepository(resourceGroup: SourceControlResourceGroup): Repository | undefined; + getRepository(path: string): Repository | undefined; + getRepository(resource: Uri): Repository | undefined; + getRepository(hint: any): Repository | undefined; +} + export class Repository implements Disposable { private _onDidChangeRepository = new EventEmitter(); @@ -784,6 +792,7 @@ export class Repository implements Disposable { constructor( private readonly repository: BaseRepository, + private readonly repositoryResolver: IRepositoryResolver, private pushErrorHandlerRegistry: IPushErrorHandlerRegistry, remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry, postCommitCommandsProviderRegistry: IPostCommitCommandsProviderRegistry, @@ -1010,13 +1019,13 @@ export class Repository implements Disposable { return; } - // Ignore path that is inside a merge group - if (this.mergeGroup.resourceStates.some(r => r.resourceUri.path === uri.path)) { + // Ignore path that is not inside the current repository + if (this.repositoryResolver.getRepository(uri) !== this) { return undefined; } - // Ignore path that is inside a submodule - if (this.submodules.some(s => isDescendant(path.join(this.repository.root, s.path), uri.path))) { + // Ignore path that is inside a merge group + if (this.mergeGroup.resourceStates.some(r => r.resourceUri.path === uri.path)) { return undefined; } From 908d5f155daa62529f739475848950615ba4b28f Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 6 Sep 2023 15:20:49 +0200 Subject: [PATCH 555/607] variable renaming --- .../lib/stylelint/vscode-known-variables.json | 2 +- .../parts/editor/media/notabstitlecontrol.css | 63 +-- .../parts/editor/media/tabstitlecontrol.css | 374 ++++++++++-------- .../parts/editor/media/titlecontrol.css | 34 +- .../browser/parts/editor/tabsTitleControl.ts | 10 +- .../browser/parts/editor/titleControl.ts | 14 +- 6 files changed, 272 insertions(+), 225 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index f10da52142c..b7d31b812d8 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -734,7 +734,7 @@ "--tab-sizing-current-width", "--tab-sizing-fixed-min-width", "--tab-sizing-fixed-max-width", - "--title-height", + "--editor-group-title-height", "--testMessageDecorationFontFamily", "--testMessageDecorationFontSize", "--title-border-bottom-color", diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index 5bfc46c255c..a8896eea06f 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -5,8 +5,8 @@ /* Title Label */ -.monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container { - height: var(--title-height); +.monaco-workbench .part.editor>.content .editor-group-container>.title>.label-container { + height: var(--editor-group-title-height); display: flex; justify-content: flex-start; align-items: center; @@ -14,39 +14,40 @@ flex: auto; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container > .title-label { - line-height: var(--title-height); +.monaco-workbench .part.editor>.content .editor-group-container>.title>.label-container>.title-label { + line-height: var(--editor-group-title-height); overflow: hidden; text-overflow: ellipsis; position: relative; padding-left: 20px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container > .title-label > .monaco-icon-label-container { - flex: initial; /* helps to show decorations right next to the label and not at the end while still preserving text overflow ellipsis */ +.monaco-workbench .part.editor>.content .editor-group-container>.title>.label-container>.title-label>.monaco-icon-label-container { + flex: initial; + /* helps to show decorations right next to the label and not at the end while still preserving text overflow ellipsis */ } /* Breadcrumbs */ -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .no-tabs.title-label { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .no-tabs.title-label { flex: none; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control { flex: 1 50%; overflow: hidden; margin-left: .45em; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { font-size: 0.9em; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item { font-style: italic; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { content: '/'; opacity: 1; height: inherit; @@ -54,53 +55,57 @@ background-image: none; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.backslash-path .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control.backslash-path .monaco-breadcrumb-item::before { content: '\\'; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before, -.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before { - display: none; /* workspace folder, item following workspace folder, or relative path -> hide first seperator */ +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder+.monaco-breadcrumb-item::before, +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before, +.monaco-workbench.windows .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before { + display: none; + /* workspace folder, item following workspace folder, or relative path -> hide first seperator */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { - content: '\00a0•\00a0'; /* use dot separator for workspace folder */ +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { + content: '\00a0•\00a0'; + /* use dot separator for workspace folder */ padding: 0; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { - padding-right: 4px; /* does not have trailing separator*/ +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { + padding-right: 4px; + /* does not have trailing separator*/ } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon[class*='codicon-symbol-'] { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon[class*='codicon-symbol-'] { padding: 0 1px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon:last-child { - display: none; /* hides chevrons when no tabs visible */ +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon:last-child { + display: none; + /* hides chevrons when no tabs visible */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-icon-label::before { +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-icon-label::before { height: 18px; padding-right: 2px; } /* Editor Actions Toolbar (via title actions) */ -.monaco-workbench .part.editor > .content .editor-group-container > .title > .title-actions { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.title-actions { display: flex; flex: initial; opacity: 0.5; padding-right: 8px; - height: var(--title-height); + height: var(--editor-group-title-height); } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .title-actions .action-item { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.title-actions .action-item { margin-right: 4px; } -.monaco-workbench .part.editor > .content .editor-group-container.active > .title > .title-actions { +.monaco-workbench .part.editor>.content .editor-group-container.active>.title>.title-actions { opacity: 1; } diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 68375000e58..77a120cec7c 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -41,12 +41,13 @@ /* Title Container */ -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container { display: flex; - position: relative; /* position tabs border bottom or editor actions (when tabs wrap) relative to this container */ + position: relative; + /* position tabs border bottom or editor actions (when tabs wrap) relative to this container */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.tabs-border-bottom::after { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.tabs-border-bottom::after { content: ''; position: absolute; bottom: 0; @@ -58,101 +59,110 @@ height: 1px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container > .monaco-scrollable-element { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container>.monaco-scrollable-element { flex: 1; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container > .monaco-scrollable-element .scrollbar { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container>.monaco-scrollable-element .scrollbar { z-index: 11; cursor: default; } /* Tabs Container */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container { display: flex; - height: var(--title-height); - scrollbar-width: none; /* Firefox: hide scrollbar */ + height: var(--editor-group-title-height); + scrollbar-width: none; + /* Firefox: hide scrollbar */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.scroll { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.scroll { overflow: scroll !important; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container { /* Enable wrapping via flex layout and dynamic height */ height: auto; flex-wrap: wrap; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container::-webkit-scrollbar { - display: none; /* Chrome + Safari: hide scrollbar */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container::-webkit-scrollbar { + display: none; + /* Chrome + Safari: hide scrollbar */ } /* Tab */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab { position: relative; display: flex; white-space: nowrap; cursor: pointer; - height: var(--title-height); + height: var(--editor-group-title-height); box-sizing: border-box; padding-left: 10px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab:last-child { - margin-right: var(--last-tab-margin-right); /* when tabs wrap, we need a margin away from the absolute positioned editor actions */ +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container>.tab:last-child { + margin-right: var(--last-tab-margin-right); + /* when tabs wrap, we need a margin away from the absolute positioned editor actions */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.last-in-row:not(:last-child) { - border-right: 0 !important; /* ensure no border for every last tab in a row except last row (#115046) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container>.tab.last-in-row:not(:last-child) { + border-right: 0 !important; + /* ensure no border for every last tab in a row except last row (#115046) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-right, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact), -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-right, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-off:not(.sticky-compact) { - padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.has-icon.tab-actions-right, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact), +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.has-icon.tab-actions-right, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.has-icon.tab-actions-off:not(.sticky-compact) { + padding-left: 5px; + /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit { width: 120px; min-width: fit-content; flex-shrink: 0; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed { min-width: var(--tab-sizing-current-width, var(--tab-sizing-fixed-min-width, 50px)); max-width: var(--tab-sizing-current-width, var(--tab-sizing-fixed-max-width, 160px)); - flex: 1 0 0; /* all tabs are evenly sized and grow */ + flex: 1 0 0; + /* all tabs are evenly sized and grow */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.last-in-row { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.last-in-row { /* prevent last tab in a row from moving to next row when tab widths are * fixed in case rounding errors make the fixed tabs grow over the size * of the tabs container */ min-width: calc(var(--tab-sizing-current-width, var(--tab-sizing-fixed-min-width, 50px)) - 1px); } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.sizing-fit.last-in-row:not(:last-child) { - flex-grow: 1; /* grow the last tab in a row for a more homogeneous look except for last row (#113801) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container>.tab.sizing-fit.last-in-row:not(:last-child) { + flex-grow: 1; + /* grow the last tab in a row for a more homogeneous look except for last row (#113801) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink { min-width: 80px; - flex-basis: 0; /* all tabs are even */ - flex-grow: 1; /* all tabs grow even */ + flex-basis: 0; + /* all tabs are even */ + flex-grow: 1; + /* all tabs grow even */ max-width: fit-content; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-shrink { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-shrink, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-shrink { /** Sticky compact/shrink/fixed tabs do not scroll in case of overflow and are always above unsticky tabs which scroll under */ position: sticky; @@ -163,9 +173,9 @@ flex-grow: 0; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-compact { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-compact { /** Sticky compact tabs have a fixed width of 38px */ width: 38px; @@ -173,9 +183,9 @@ max-width: 38px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-shrink { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-shrink, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-shrink { /** Sticky shrink tabs have a fixed width of 80px */ width: 80px; @@ -183,40 +193,46 @@ max-width: 80px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fit.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-shrink.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fixed.sticky-compact, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-shrink.sticky-shrink, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fixed.sticky-shrink { - position: static; /** disable sticky positions for sticky compact/shrink/fixed tabs if the available space is too little */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fit.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fixed.sticky-compact, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fit.sticky-shrink, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fixed.sticky-shrink { + position: static; + /** disable sticky positions for sticky compact/shrink/fixed tabs if the available space is too little */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off::after { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-left::after, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-off::after, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-left::after, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-off::after { content: ''; display: flex; flex: 0; - width: 5px; /* reserve space to hide tab fade when close button is left or off (fixes https://github.com/microsoft/vscode/issues/45728) */ + width: 5px; + /* reserve space to hide tab fade when close button is left or off (fixes https://github.com/microsoft/vscode/issues/45728) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left { - min-width: 80px; /* make more room for close button when it shows to the left */ - padding-right: 5px; /* we need less room when sizing is shrink/fixed */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-left, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-left { + min-width: 80px; + /* make more room for close button when it shows to the left */ + padding-right: 5px; + /* we need less room when sizing is shrink/fixed */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged { - transform: translate3d(0px, 0px, 0px); /* forces tab to be drawn on a separate layer (fixes https://github.com/microsoft/vscode/issues/18733) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dragged { + transform: translate3d(0px, 0px, 0px); + /* forces tab to be drawn on a separate layer (fixes https://github.com/microsoft/vscode/issues/18733) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged-over div { - pointer-events: none; /* prevents cursor flickering (fixes https://github.com/microsoft/vscode/issues/38753) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dragged-over div { + pointer-events: none; + /* prevents cursor flickering (fixes https://github.com/microsoft/vscode/issues/38753) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-left { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-left { flex-direction: row-reverse; padding-left: 0; padding-right: 10px; @@ -224,14 +240,15 @@ /* Tab border top/bottom */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container { - display: none; /* hidden by default until a color is provided (see below) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-border-top-container, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-border-bottom-container { + display: none; + /* hidden by default until a color is provided (see below) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-top>.tab-border-top-container, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-bottom>.tab-border-bottom-container, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty-border-top>.tab-border-top-container { display: block; position: absolute; left: 0; @@ -239,21 +256,21 @@ width: 100%; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-top>.tab-border-top-container { z-index: 6; top: 0; height: 1px; background-color: var(--tab-border-top-color); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-bottom>.tab-border-bottom-container { z-index: 10; bottom: 0; height: 1px; background-color: var(--tab-border-bottom-color); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty-border-top>.tab-border-top-container { z-index: 6; top: 0; height: 2px; @@ -262,20 +279,22 @@ /* Tab Label */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label { margin-top: auto; margin-bottom: auto; - line-height: var(--title-height); /* aligns icon and label vertically centered in the tab */ + line-height: var(--editor-group-title-height); + /* aligns icon and label vertically centered in the tab */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed .tab-label { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink .tab-label, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed .tab-label { position: relative; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label > .monaco-icon-label-container::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .tab-label > .monaco-icon-label-container::after { - content: ''; /* enables a linear gradient to overlay the end of the label when tabs overflow */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.tab-label>.monaco-icon-label-container::after, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.tab-label>.monaco-icon-label-container::after { + content: ''; + /* enables a linear gradient to overlay the end of the label when tabs overflow */ position: absolute; right: 0; width: 5px; @@ -287,90 +306,104 @@ height: calc(100% - 2px); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:focus > .tab-label > .monaco-icon-label-container::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed:focus > .tab-label > .monaco-icon-label-container::after { - opacity: 0; /* when tab has the focus this shade breaks the tab border (fixes https://github.com/microsoft/vscode/issues/57819) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink:focus>.tab-label>.monaco-icon-label-container::after, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed:focus>.tab-label>.monaco-icon-label-container::after { + opacity: 0; + /* when tab has the focus this shade breaks the tab border (fixes https://github.com/microsoft/vscode/issues/57819) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label.tab-label-has-badge::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .tab-label.tab-label-has-badge::after { - padding-right: 5px; /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.tab-label.tab-label-has-badge::after, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.tab-label.tab-label-has-badge::after { + padding-right: 5px; + /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.tab-actions-left):not(.tab-actions-off) .tab-label { - padding-right: 5px; /* ensure that the gradient does not show when tab actions show https://github.com/microsoft/vscode/issues/189625*/ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink:not(.tab-actions-left):not(.tab-actions-off) .tab-label { + padding-right: 5px; + /* ensure that the gradient does not show when tab actions show https://github.com/microsoft/vscode/issues/189625*/ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky-compact:not(.has-icon) .monaco-icon-label { - text-align: center; /* ensure that sticky-compact tabs without icon have label centered */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky-compact:not(.has-icon) .monaco-icon-label { + text-align: center; + /* ensure that sticky-compact tabs without icon have label centered */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-container { - overflow: visible; /* fixes https://github.com/microsoft/vscode/issues/20182 */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit .monaco-icon-label, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit .monaco-icon-label>.monaco-icon-label-container { + overflow: visible; + /* fixes https://github.com/microsoft/vscode/issues/20182 */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.monaco-icon-label>.monaco-icon-label-container, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.monaco-icon-label>.monaco-icon-label-container { text-overflow: clip; flex: none; } -.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, -.monaco-workbench.hc-light .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, -.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container, -.monaco-workbench.hc-light .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container { +.monaco-workbench.hc-black .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.monaco-icon-label>.monaco-icon-label-container, +.monaco-workbench.hc-light .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.monaco-icon-label>.monaco-icon-label-container, +.monaco-workbench.hc-black .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.monaco-icon-label>.monaco-icon-label-container, +.monaco-workbench.hc-light .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.monaco-icon-label>.monaco-icon-label-container { text-overflow: ellipsis; } /* Tab Actions */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions { margin-top: auto; margin-bottom: auto; width: 28px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions > .monaco-action-bar { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions>.monaco-action-bar { width: 28px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed > .tab-actions { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-shrink>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-fixed>.tab-actions { flex: 0; - overflow: hidden; /* let the tab actions be pushed out of view when sizing is set to shrink/fixed to make more room */ + overflow: hidden; + /* let the tab actions be pushed out of view when sizing is set to shrink/fixed to make more room */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.tab-actions-right.sizing-shrink > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.tab-actions-right.sizing-shrink > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink:hover > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions:focus-within, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.tab-actions-right.sizing-fixed > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.tab-actions-right.sizing-fixed > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed:hover > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed > .tab-actions:focus-within { - overflow: visible; /* ...but still show the tab actions on hover, focus and when dirty or sticky */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty.tab-actions-right.sizing-shrink>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky.tab-actions-right.sizing-shrink>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-shrink:hover>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-shrink>.tab-actions:focus-within, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty.tab-actions-right.sizing-fixed>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky.tab-actions-right.sizing-fixed>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-fixed:hover>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-fixed>.tab-actions:focus-within { + overflow: visible; + /* ...but still show the tab actions on hover, focus and when dirty or sticky */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off:not(.dirty):not(.sticky) > .tab-actions, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.sticky-compact > .tab-actions { - display: none; /* hide the tab actions when we are configured to hide it (unless dirty or sticky, but always when sticky-compact) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off:not(.dirty):not(.sticky)>.tab-actions, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.sticky-compact>.tab-actions { + display: none; + /* hide the tab actions when we are configured to hide it (unless dirty or sticky, but always when sticky-compact) */ } -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-actions .action-label, /* always show tab actions for active tab */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-actions .action-label:focus, /* always show tab actions on focus */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-actions .action-label, /* always show tab actions on hover */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-actions .action-label, /* always show tab actions on hover */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky > .tab-actions .action-label, /* always show tab actions for sticky tabs */ -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label { /* always show tab actions for dirty tabs */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.active>.tab-actions .action-label, +/* always show tab actions for active tab */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab>.tab-actions .action-label:focus, +/* always show tab actions on focus */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab:hover>.tab-actions .action-label, +/* always show tab actions on hover */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.active:hover>.tab-actions .action-label, +/* always show tab actions on hover */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.sticky>.tab-actions .action-label, +/* always show tab actions for sticky tabs */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.dirty>.tab-actions .action-label { + /* always show tab actions for dirty tabs */ opacity: 1; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions .actions-container { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions .actions-container { justify-content: center; } -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-actions .action-label.codicon { +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab>.tab-actions .action-label.codicon { color: inherit; font-size: 16px; padding: 2px; @@ -378,80 +411,90 @@ height: 16px; } -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky.dirty > .tab-actions .action-label:not(:hover)::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.dirty > .tab-actions .action-label:not(:hover)::before { - content: "\ebb2"; /* use `pinned-dirty` icon unicode for sticky-dirty indication */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.sticky.dirty>.tab-actions .action-label:not(:hover)::before, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky.dirty>.tab-actions .action-label:not(:hover)::before { + content: "\ebb2"; + /* use `pinned-dirty` icon unicode for sticky-dirty indication */ } -.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before { - content: "\ea71"; /* use `circle-filled` icon unicode for dirty indication */ +.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.dirty>.tab-actions .action-label:not(:hover)::before, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty>.tab-actions .action-label:not(:hover)::before { + content: "\ea71"; + /* use `circle-filled` icon unicode for dirty indication */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-actions .action-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-actions .action-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky > .tab-actions .action-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-actions .action-label { - opacity: 0.5; /* show tab actions dimmed for inactive group */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active>.tab-actions .action-label, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active:hover>.tab-actions .action-label, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty>.tab-actions .action-label, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky>.tab-actions .action-label, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab:hover>.tab-actions .action-label { + opacity: 0.5; + /* show tab actions dimmed for inactive group */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions .action-label { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions .action-label { opacity: 0; } /* Tab Actions: Off */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off { - padding-right: 10px; /* give a little bit more room if tab actions is off */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off { + padding-right: 10px; + /* give a little bit more room if tab actions is off */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off:not(.sticky-compact), -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off:not(.sticky-compact) { - padding-right: 5px; /* we need less room when sizing is shrink/fixed (unless tab is sticky-compact) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-off:not(.sticky-compact), +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-off:not(.sticky-compact) { + padding-right: 5px; + /* we need less room when sizing is shrink/fixed (unless tab is sticky-compact) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty-border-top > .tab-actions { - display: none; /* hide dirty state when highlightModifiedTabs is enabled and when running without tab actions */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.dirty-border-top>.tab-actions { + display: none; + /* hide dirty state when highlightModifiedTabs is enabled and when running without tab actions */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty:not(.dirty-border-top):not(.sticky-compact), -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.sticky:not(.sticky-compact) { - padding-right: 0; /* remove extra padding when we are running without tab actions (unless tab is sticky-compact) */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.dirty:not(.dirty-border-top):not(.sticky-compact), +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.sticky:not(.sticky-compact) { + padding-right: 0; + /* remove extra padding when we are running without tab actions (unless tab is sticky-compact) */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off > .tab-actions { - pointer-events: none; /* don't allow tab actions to be clicked when running without tab actions */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off>.tab-actions { + pointer-events: none; + /* don't allow tab actions to be clicked when running without tab actions */ } /* Breadcrumbs */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control { flex: 1 100%; height: 22px; cursor: default; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label { height: 22px; line-height: 22px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before { - height: 22px; /* tweak the icon size of the editor labels when icons are enabled */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before { + height: 22px; + /* tweak the icon size of the editor labels when icons are enabled */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .outline-element-icon { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .outline-element-icon { padding-right: 3px; - height: 22px; /* tweak the icon size of the editor labels when icons are enabled */ + height: 22px; + /* tweak the icon size of the editor labels when icons are enabled */ line-height: 22px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { max-width: 80%; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { width: 16px; height: 22px; display: flex; @@ -459,28 +502,29 @@ justify-content: center; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { padding-right: 8px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child .codicon:last-child { - display: none; /* hides chevrons when last item */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child .codicon:last-child { + display: none; + /* hides chevrons when last item */ } /* Editor Actions Toolbar */ -.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions { +.monaco-workbench .part.editor>.content .editor-group-container>.title .editor-actions { cursor: default; flex: initial; padding: 0 8px 0 4px; - height: var(--title-height); + height: var(--editor-group-title-height); } -.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-item { +.monaco-workbench .part.editor>.content .editor-group-container>.title .editor-actions .action-item { margin-right: 4px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .editor-actions { +.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .editor-actions { /* When tabs are wrapped, position the editor actions at the end of the very last row */ position: absolute; diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 2ab7ffcc5c3..ff27cffffb2 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -5,37 +5,39 @@ /* Editor Label */ -.monaco-workbench .part.editor > .content .editor-group-container > .title { +.monaco-workbench .part.editor>.content .editor-group-container>.title { cursor: pointer; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { +.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label { white-space: nowrap; flex: 1; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label a, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a { +.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label a, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label a { font-size: 13px; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before, -.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label a, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a, -.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label h2, -.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span { +.monaco-workbench .part.editor>.content .editor-group-container>.title .monaco-icon-label::before, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .monaco-icon-label::before, +.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label a, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label a, +.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label h2, +.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label span { cursor: pointer; } -.monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before { - height: var(--title-height); /* tweak the icon size of the editor labels when icons are enabled */ +.monaco-workbench .part.editor>.content .editor-group-container>.title .monaco-icon-label::before { + height: var(--editor-group-title-height); + /* tweak the icon size of the editor labels when icons are enabled */ } -.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .monaco-icon-label::after, -.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .monaco-icon-label::after { - margin-right: 0; /* by default the icon label has a padding right and this isn't wanted when not showing tabs and not showing breadcrumbs */ +.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .monaco-icon-label::after, +.monaco-workbench .part.editor>.content .editor-group-container>.title.tabs .monaco-icon-label::after { + margin-right: 0; + /* by default the icon label has a padding right and this isn't wanted when not showing tabs and not showing breadcrumbs */ } /* Drag and Drop */ diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index ed3875f0cd5..5d6b14d633a 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -203,7 +203,6 @@ export class TabsTitleControl extends TitleControl { breadcrumbsContainer.classList.add('tabs-breadcrumbs'); this.titleContainer.appendChild(breadcrumbsContainer); this.createBreadcrumbsControl(breadcrumbsContainer, { showFileIcons: true, showSymbolIcons: true, showDecorationColors: false, showPlaceholder: true }); - } private createTabsScrollbar(scrollable: HTMLElement): ScrollableElement { @@ -733,11 +732,6 @@ export class TabsTitleControl extends TitleControl { this.updateTabSizing(true); } - // Update tab height - if (oldOptions.tabHeight !== newOptions.tabHeight) { - this.updateTitleHeight(); - } - // Redraw tabs when other options change if ( oldOptions.labelFormat !== newOptions.labelFormat || @@ -1556,7 +1550,7 @@ export class TabsTitleControl extends TitleControl { if (this.accessor.partOptions.wrapTabs && this.tabsAndActionsContainer?.classList.contains('wrapping')) { total = this.tabsAndActionsContainer.offsetHeight; } else { - total = this.tabHeight; + total = this.titleHeight; } const offset = total; @@ -1714,7 +1708,7 @@ export class TabsTitleControl extends TitleControl { if (tabsWrapMultiLine) { if ( (tabsContainer.offsetHeight > dimensions.available.height) || // if height exceeds available height - (allTabsWidth === visibleTabsWidth && tabsContainer.offsetHeight === this.tabHeight) || // if wrapping is not needed anymore + (allTabsWidth === visibleTabsWidth && tabsContainer.offsetHeight === this.titleHeight) || // if wrapping is not needed anymore (!lastTabFitsWrapped()) // if last tab does not fit anymore ) { updateTabsWrapping(false); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index e3a17ba3705..22187e0daee 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -93,8 +93,10 @@ export abstract class TitleControl extends Themable { protected readonly groupTransfer = LocalSelectionTransfer.getInstance(); protected readonly treeItemsTransfer = LocalSelectionTransfer.getInstance(); - private static readonly EDITOR_TITLE_NORMAL = 35; - private static readonly EDITOR_TITLE_COMPACT = 22; + private static readonly EDITOR_TITLE_HEIGHT = { + normal: 35, + compact: 22 + }; protected breadcrumbsControl: BreadcrumbsControl | undefined = undefined; @@ -118,7 +120,7 @@ export abstract class TitleControl extends Themable { private renderDropdownAsChildElement: boolean; constructor( - protected parent: HTMLElement, + private parent: HTMLElement, protected accessor: IEditorGroupsAccessor, protected group: IEditorGroupView, @IContextMenuService protected readonly contextMenuService: IContextMenuService, @@ -427,12 +429,12 @@ export abstract class TitleControl extends Themable { return keybinding ? keybinding.getLabel() ?? undefined : undefined; } - protected get tabHeight() { - return this.accessor.partOptions.tabHeight !== 'compact' ? TitleControl.EDITOR_TITLE_NORMAL : TitleControl.EDITOR_TITLE_COMPACT; + protected get titleHeight() { + return this.accessor.partOptions.tabHeight !== 'compact' ? TitleControl.EDITOR_TITLE_HEIGHT.normal : TitleControl.EDITOR_TITLE_HEIGHT.compact; } protected updateTitleHeight(): void { - this.parent.style.setProperty('--title-height', `${this.tabHeight}px`); + this.parent.style.setProperty('--editor-group-title-height', `${this.titleHeight}px`); } updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { From bde132b4412fca35c217f60c657bbc1de46d756f Mon Sep 17 00:00:00 2001 From: Michael Bethke Date: Wed, 6 Sep 2023 08:23:04 -0500 Subject: [PATCH 556/607] Fix Minimap AutoHide with StickyScroll Lines Running Together (#188499) * Layer minimap above stickyscroll * StickyScroll Allow Full-Width and Layer Under * Restore stickyscroll zindex * Increase minimap z-index * Restore stickyscroll zindex --- .../browser/viewParts/minimap/minimap.css | 4 ++++ .../browser/stickyScrollWidget.ts | 23 +++++-------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.css b/src/vs/editor/browser/viewParts/minimap/minimap.css index 6861bdf0186..d09f089b081 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.css +++ b/src/vs/editor/browser/viewParts/minimap/minimap.css @@ -50,3 +50,7 @@ .minimap.autohide:hover { opacity: 1; } + +.monaco-editor .minimap { + z-index: 5; +} diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 9a47963427b..763c3c862db 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -133,11 +133,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private _updateWidgetWidth(): void { const layoutInfo = this._editor.getLayoutInfo(); - const minimapSide = this._editor.getOption(EditorOption.minimap).side; - const lineNumbersWidth = minimapSide === 'left' ? layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth : layoutInfo.contentLeft; + const lineNumbersWidth = layoutInfo.contentLeft; this._lineNumbersDomNode.style.width = `${lineNumbersWidth}px`; this._linesDomNodeScrollable.style.setProperty('--vscode-editorStickyScroll-scrollableWidth', `${this._editor.getScrollWidth() - layoutInfo.verticalScrollbarWidth}px`); - this._rootDomNode.style.width = `${layoutInfo.width - layoutInfo.minimap.minimapCanvasOuterWidth - layoutInfo.verticalScrollbarWidth}px`; + this._rootDomNode.style.width = `${layoutInfo.width - layoutInfo.verticalScrollbarWidth}px`; } private _clearStickyWidget() { @@ -189,13 +188,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._lineNumbersDomNode.style.height = `${widgetHeight}px`; this._linesDomNodeScrollable.style.height = `${widgetHeight}px`; this._rootDomNode.style.height = `${widgetHeight}px`; - const minimapSide = this._editor.getOption(EditorOption.minimap).side; - if (minimapSide === 'left') { - this._rootDomNode.style.marginLeft = layoutInfo.minimap.minimapCanvasOuterWidth + 'px'; - } else { - this._rootDomNode.style.marginLeft = '0px'; - } + this._rootDomNode.style.marginLeft = '0px'; this._updateMinContentWidth(); this._editor.layoutOverlayWidget(this); } @@ -224,7 +218,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { } const viewLineNumber = viewModel.coordinatesConverter.convertModelPositionToViewPosition(new Position(line, 1)).lineNumber; const lineRenderingData = viewModel!.getViewLineRenderingData(viewLineNumber); - const minimapSide = this._editor.getOption(EditorOption.minimap).side; const lineNumberOption = this._editor.getOption(EditorOption.lineNumbers); let actualInlineDecorations: LineDecoration[]; @@ -261,7 +254,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const lineNumberHTMLNode = document.createElement('span'); lineNumberHTMLNode.className = 'sticky-line-number'; lineNumberHTMLNode.style.lineHeight = `${this._lineHeight}px`; - const lineNumbersWidth = minimapSide === 'left' ? layoutInfo.contentLeft - layoutInfo.minimap.minimapCanvasOuterWidth : layoutInfo.contentLeft; + const lineNumbersWidth = layoutInfo.contentLeft; lineNumberHTMLNode.style.width = `${lineNumbersWidth}px`; const innerLineNumberHTML = document.createElement('span'); @@ -273,12 +266,8 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { innerLineNumberHTML.className = 'sticky-line-number-inner'; innerLineNumberHTML.style.lineHeight = `${this._lineHeight}px`; innerLineNumberHTML.style.width = `${layoutInfo.lineNumbersWidth}px`; - innerLineNumberHTML.style.float = 'left'; - if (minimapSide === 'left') { - innerLineNumberHTML.style.paddingLeft = `${layoutInfo.lineNumbersLeft - layoutInfo.minimap.minimapCanvasOuterWidth}px`; - } else if (minimapSide === 'right') { - innerLineNumberHTML.style.paddingLeft = `${layoutInfo.lineNumbersLeft}px`; - } + innerLineNumberHTML.style.paddingLeft = `${layoutInfo.lineNumbersLeft}px`; + lineNumberHTMLNode.appendChild(innerLineNumberHTML); const foldingIcon = this._renderFoldingIconForLine(lineNumberHTMLNode, foldingModel, index, line); From 79a9276e6a0cbbb8d19a720f87af3c3098a9380a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 6 Sep 2023 15:22:30 +0200 Subject: [PATCH 557/607] Uses disposable tracking for autorun --- src/vs/base/common/lifecycle.ts | 4 ++-- src/vs/base/common/observableInternal/autorun.ts | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index c044d998203..1682b7932ae 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -80,12 +80,12 @@ if (TRACK_DISPOSABLES) { }); } -function trackDisposable(x: T): T { +export function trackDisposable(x: T): T { disposableTracker?.trackDisposable(x); return x; } -function markAsDisposed(disposable: IDisposable): void { +export function markAsDisposed(disposable: IDisposable): void { disposableTracker?.markAsDisposed(disposable); } diff --git a/src/vs/base/common/observableInternal/autorun.ts b/src/vs/base/common/observableInternal/autorun.ts index 482e592d883..050e9bae1c3 100644 --- a/src/vs/base/common/observableInternal/autorun.ts +++ b/src/vs/base/common/observableInternal/autorun.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { assertFn } from 'vs/base/common/assert'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, markAsDisposed, toDisposable, trackDisposable } from 'vs/base/common/lifecycle'; import { IReader, IObservable, IObserver, IChangeContext, getFunctionName } from 'vs/base/common/observableInternal/base'; import { getLogger } from 'vs/base/common/observableInternal/logging'; @@ -115,6 +115,8 @@ export class AutorunObserver implements IObserver, IReader this.changeSummary = this.createChangeSummary?.(); getLogger()?.handleAutorunCreated(this); this._runIfNeeded(); + + trackDisposable(this); } public dispose(): void { @@ -123,6 +125,8 @@ export class AutorunObserver implements IObserver, IReader o.removeObserver(this); } this.dependencies.clear(); + + markAsDisposed(this); } private _runIfNeeded() { From ebf1fa42c5c13cc5bf8986af023589c5355dc182 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Wed, 6 Sep 2023 15:46:01 +0200 Subject: [PATCH 558/607] reformat --- .../parts/editor/media/notabstitlecontrol.css | 57 ++- .../parts/editor/media/tabstitlecontrol.css | 368 ++++++++---------- .../parts/editor/media/titlecontrol.css | 34 +- 3 files changed, 204 insertions(+), 255 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css index a8896eea06f..81562a81281 100644 --- a/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/notabstitlecontrol.css @@ -5,7 +5,7 @@ /* Title Label */ -.monaco-workbench .part.editor>.content .editor-group-container>.title>.label-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container { height: var(--editor-group-title-height); display: flex; justify-content: flex-start; @@ -14,7 +14,7 @@ flex: auto; } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.label-container>.title-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container > .title-label { line-height: var(--editor-group-title-height); overflow: hidden; text-overflow: ellipsis; @@ -22,32 +22,31 @@ padding-left: 20px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.label-container>.title-label>.monaco-icon-label-container { - flex: initial; - /* helps to show decorations right next to the label and not at the end while still preserving text overflow ellipsis */ +.monaco-workbench .part.editor > .content .editor-group-container > .title > .label-container > .title-label > .monaco-icon-label-container { + flex: initial; /* helps to show decorations right next to the label and not at the end while still preserving text overflow ellipsis */ } /* Breadcrumbs */ -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .no-tabs.title-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .no-tabs.title-label { flex: none; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control { flex: 1 50%; overflow: hidden; margin-left: .45em; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { font-size: 0.9em; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.preview .monaco-breadcrumb-item { font-style: italic; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { content: '/'; opacity: 1; height: inherit; @@ -55,46 +54,42 @@ background-image: none; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control.backslash-path .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.backslash-path .monaco-breadcrumb-item::before { content: '\\'; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder+.monaco-breadcrumb-item::before, -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before, -.monaco-workbench.windows .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before { - display: none; - /* workspace folder, item following workspace folder, or relative path -> hide first seperator */ +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before, +.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before { + display: none; /* workspace folder, item following workspace folder, or relative path -> hide first seperator */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { - content: '\00a0•\00a0'; - /* use dot separator for workspace folder */ +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::after { + content: '\00a0•\00a0'; /* use dot separator for workspace folder */ padding: 0; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { - padding-right: 4px; - /* does not have trailing separator*/ +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { + padding-right: 4px; /* does not have trailing separator*/ } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon[class*='codicon-symbol-'] { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon[class*='codicon-symbol-'] { padding: 0 1px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon:last-child { - display: none; - /* hides chevrons when no tabs visible */ +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item .codicon:last-child { + display: none; /* hides chevrons when no tabs visible */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .breadcrumbs-control .monaco-icon-label::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-icon-label::before { height: 18px; padding-right: 2px; } /* Editor Actions Toolbar (via title actions) */ -.monaco-workbench .part.editor>.content .editor-group-container>.title>.title-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .title-actions { display: flex; flex: initial; opacity: 0.5; @@ -102,10 +97,10 @@ height: var(--editor-group-title-height); } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.title-actions .action-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .title-actions .action-item { margin-right: 4px; } -.monaco-workbench .part.editor>.content .editor-group-container.active>.title>.title-actions { +.monaco-workbench .part.editor > .content .editor-group-container.active > .title > .title-actions { opacity: 1; } diff --git a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css index 77a120cec7c..f5b39db85fc 100644 --- a/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/tabstitlecontrol.css @@ -41,13 +41,12 @@ /* Title Container */ -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container { display: flex; - position: relative; - /* position tabs border bottom or editor actions (when tabs wrap) relative to this container */ + position: relative; /* position tabs border bottom or editor actions (when tabs wrap) relative to this container */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.tabs-border-bottom::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.tabs-border-bottom::after { content: ''; position: absolute; bottom: 0; @@ -59,43 +58,41 @@ height: 1px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container>.monaco-scrollable-element { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container > .monaco-scrollable-element { flex: 1; } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container>.monaco-scrollable-element .scrollbar { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container > .monaco-scrollable-element .scrollbar { z-index: 11; cursor: default; } /* Tabs Container */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container { display: flex; height: var(--editor-group-title-height); - scrollbar-width: none; - /* Firefox: hide scrollbar */ + scrollbar-width: none; /* Firefox: hide scrollbar */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.scroll { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.scroll { overflow: scroll !important; } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container { /* Enable wrapping via flex layout and dynamic height */ height: auto; flex-wrap: wrap; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container::-webkit-scrollbar { - display: none; - /* Chrome + Safari: hide scrollbar */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container::-webkit-scrollbar { + display: none; /* Chrome + Safari: hide scrollbar */ } /* Tab */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab { position: relative; display: flex; white-space: nowrap; @@ -105,64 +102,57 @@ padding-left: 10px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container>.tab:last-child { - margin-right: var(--last-tab-margin-right); - /* when tabs wrap, we need a margin away from the absolute positioned editor actions */ +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab:last-child { + margin-right: var(--last-tab-margin-right); /* when tabs wrap, we need a margin away from the absolute positioned editor actions */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container>.tab.last-in-row:not(:last-child) { - border-right: 0 !important; - /* ensure no border for every last tab in a row except last row (#115046) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.last-in-row:not(:last-child) { + border-right: 0 !important; /* ensure no border for every last tab in a row except last row (#115046) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.has-icon.tab-actions-right, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact), -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.has-icon.tab-actions-right, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.has-icon.tab-actions-off:not(.sticky-compact) { - padding-left: 5px; - /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-right, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.has-icon.tab-actions-off:not(.sticky-compact), +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-right, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.has-icon.tab-actions-off:not(.sticky-compact) { + padding-left: 5px; /* reduce padding when we show icons and are in shrinking mode and tab actions is not left (unless sticky-compact) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit { width: 120px; min-width: fit-content; flex-shrink: 0; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed { min-width: var(--tab-sizing-current-width, var(--tab-sizing-fixed-min-width, 50px)); max-width: var(--tab-sizing-current-width, var(--tab-sizing-fixed-max-width, 160px)); - flex: 1 0 0; - /* all tabs are evenly sized and grow */ + flex: 1 0 0; /* all tabs are evenly sized and grow */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.last-in-row { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.last-in-row { /* prevent last tab in a row from moving to next row when tab widths are * fixed in case rounding errors make the fixed tabs grow over the size * of the tabs container */ min-width: calc(var(--tab-sizing-current-width, var(--tab-sizing-fixed-min-width, 50px)) - 1px); } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .tabs-container>.tab.sizing-fit.last-in-row:not(:last-child) { - flex-grow: 1; - /* grow the last tab in a row for a more homogeneous look except for last row (#113801) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .tabs-container > .tab.sizing-fit.last-in-row:not(:last-child) { + flex-grow: 1; /* grow the last tab in a row for a more homogeneous look except for last row (#113801) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink { min-width: 80px; - flex-basis: 0; - /* all tabs are even */ - flex-grow: 1; - /* all tabs grow even */ + flex-basis: 0; /* all tabs are even */ + flex-grow: 1; /* all tabs grow even */ max-width: fit-content; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-shrink, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-shrink { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-shrink { /** Sticky compact/shrink/fixed tabs do not scroll in case of overflow and are always above unsticky tabs which scroll under */ position: sticky; @@ -173,9 +163,9 @@ flex-grow: 0; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-compact { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-compact { /** Sticky compact tabs have a fixed width of 38px */ width: 38px; @@ -183,9 +173,9 @@ max-width: 38px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.sticky-shrink, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.sticky-shrink { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.sticky-shrink { /** Sticky shrink tabs have a fixed width of 80px */ width: 80px; @@ -193,46 +183,40 @@ max-width: 80px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fit.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-shrink.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fixed.sticky-compact, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fit.sticky-shrink, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-shrink.sticky-shrink, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container.disable-sticky-tabs>.tab.sizing-fixed.sticky-shrink { - position: static; - /** disable sticky positions for sticky compact/shrink/fixed tabs if the available space is too little */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fit.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-shrink.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fixed.sticky-compact, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fit.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-shrink.sticky-shrink, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container.disable-sticky-tabs > .tab.sizing-fixed.sticky-shrink { + position: static; /** disable sticky positions for sticky compact/shrink/fixed tabs if the available space is too little */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-left::after, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-off::after, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-left::after, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-off::after { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off::after { content: ''; display: flex; flex: 0; - width: 5px; - /* reserve space to hide tab fade when close button is left or off (fixes https://github.com/microsoft/vscode/issues/45728) */ + width: 5px; /* reserve space to hide tab fade when close button is left or off (fixes https://github.com/microsoft/vscode/issues/45728) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-left, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-left { - min-width: 80px; - /* make more room for close button when it shows to the left */ - padding-right: 5px; - /* we need less room when sizing is shrink/fixed */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-left, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-left { + min-width: 80px; /* make more room for close button when it shows to the left */ + padding-right: 5px; /* we need less room when sizing is shrink/fixed */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dragged { - transform: translate3d(0px, 0px, 0px); - /* forces tab to be drawn on a separate layer (fixes https://github.com/microsoft/vscode/issues/18733) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged { + transform: translate3d(0px, 0px, 0px); /* forces tab to be drawn on a separate layer (fixes https://github.com/microsoft/vscode/issues/18733) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dragged-over div { - pointer-events: none; - /* prevents cursor flickering (fixes https://github.com/microsoft/vscode/issues/38753) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dragged-over div { + pointer-events: none; /* prevents cursor flickering (fixes https://github.com/microsoft/vscode/issues/38753) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-left { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-left { flex-direction: row-reverse; padding-left: 0; padding-right: 10px; @@ -240,15 +224,14 @@ /* Tab border top/bottom */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-border-top-container, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-border-bottom-container { - display: none; - /* hidden by default until a color is provided (see below) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-top-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-border-bottom-container { + display: none; /* hidden by default until a color is provided (see below) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-top>.tab-border-top-container, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-bottom>.tab-border-bottom-container, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty-border-top>.tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { display: block; position: absolute; left: 0; @@ -256,21 +239,21 @@ width: 100%; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-top>.tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-top > .tab-border-top-container { z-index: 6; top: 0; height: 1px; background-color: var(--tab-border-top-color); } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active.tab-border-bottom>.tab-border-bottom-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active.tab-border-bottom > .tab-border-bottom-container { z-index: 10; bottom: 0; height: 1px; background-color: var(--tab-border-bottom-color); } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty-border-top>.tab-border-top-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty-border-top > .tab-border-top-container { z-index: 6; top: 0; height: 2px; @@ -279,22 +262,20 @@ /* Tab Label */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { margin-top: auto; margin-bottom: auto; - line-height: var(--editor-group-title-height); - /* aligns icon and label vertically centered in the tab */ + line-height: var(--editor-group-title-height); /* aligns icon and label vertically centered in the tab */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink .tab-label, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink .tab-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed .tab-label { position: relative; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.tab-label>.monaco-icon-label-container::after, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.tab-label>.monaco-icon-label-container::after { - content: ''; - /* enables a linear gradient to overlay the end of the label when tabs overflow */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label > .monaco-icon-label-container::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .tab-label > .monaco-icon-label-container::after { + content: ''; /* enables a linear gradient to overlay the end of the label when tabs overflow */ position: absolute; right: 0; width: 5px; @@ -306,104 +287,90 @@ height: calc(100% - 2px); } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink:focus>.tab-label>.monaco-icon-label-container::after, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed:focus>.tab-label>.monaco-icon-label-container::after { - opacity: 0; - /* when tab has the focus this shade breaks the tab border (fixes https://github.com/microsoft/vscode/issues/57819) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:focus > .tab-label > .monaco-icon-label-container::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed:focus > .tab-label > .monaco-icon-label-container::after { + opacity: 0; /* when tab has the focus this shade breaks the tab border (fixes https://github.com/microsoft/vscode/issues/57819) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.tab-label.tab-label-has-badge::after, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.tab-label.tab-label-has-badge::after { - padding-right: 5px; - /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label.tab-label-has-badge::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .tab-label.tab-label-has-badge::after { + padding-right: 5px; /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink:not(.tab-actions-left):not(.tab-actions-off) .tab-label { - padding-right: 5px; - /* ensure that the gradient does not show when tab actions show https://github.com/microsoft/vscode/issues/189625*/ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.tab-actions-left):not(.tab-actions-off) .tab-label { + padding-right: 5px; /* ensure that the gradient does not show when tab actions show https://github.com/microsoft/vscode/issues/189625*/ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky-compact:not(.has-icon) .monaco-icon-label { - text-align: center; - /* ensure that sticky-compact tabs without icon have label centered */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky-compact:not(.has-icon) .monaco-icon-label { + text-align: center; /* ensure that sticky-compact tabs without icon have label centered */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit .monaco-icon-label, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fit .monaco-icon-label>.monaco-icon-label-container { - overflow: visible; - /* fixes https://github.com/microsoft/vscode/issues/20182 */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-container { + overflow: visible; /* fixes https://github.com/microsoft/vscode/issues/20182 */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.monaco-icon-label>.monaco-icon-label-container, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.monaco-icon-label>.monaco-icon-label-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container { text-overflow: clip; flex: none; } -.monaco-workbench.hc-black .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.monaco-icon-label>.monaco-icon-label-container, -.monaco-workbench.hc-light .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink>.monaco-icon-label>.monaco-icon-label-container, -.monaco-workbench.hc-black .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.monaco-icon-label>.monaco-icon-label-container, -.monaco-workbench.hc-light .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed>.monaco-icon-label>.monaco-icon-label-container { +.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, +.monaco-workbench.hc-light .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, +.monaco-workbench.hc-black .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container, +.monaco-workbench.hc-light .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .monaco-icon-label > .monaco-icon-label-container { text-overflow: ellipsis; } /* Tab Actions */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions { margin-top: auto; margin-bottom: auto; width: 28px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions>.monaco-action-bar { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions > .monaco-action-bar { width: 28px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-shrink>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-fixed>.tab-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed > .tab-actions { flex: 0; - overflow: hidden; - /* let the tab actions be pushed out of view when sizing is set to shrink/fixed to make more room */ + overflow: hidden; /* let the tab actions be pushed out of view when sizing is set to shrink/fixed to make more room */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty.tab-actions-right.sizing-shrink>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky.tab-actions-right.sizing-shrink>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-shrink:hover>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-shrink>.tab-actions:focus-within, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty.tab-actions-right.sizing-fixed>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky.tab-actions-right.sizing-fixed>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-fixed:hover>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-right.sizing-fixed>.tab-actions:focus-within { - overflow: visible; - /* ...but still show the tab actions on hover, focus and when dirty or sticky */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.tab-actions-right.sizing-shrink > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.tab-actions-right.sizing-shrink > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink:hover > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-shrink > .tab-actions:focus-within, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty.tab-actions-right.sizing-fixed > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.tab-actions-right.sizing-fixed > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed:hover > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-right.sizing-fixed > .tab-actions:focus-within { + overflow: visible; /* ...but still show the tab actions on hover, focus and when dirty or sticky */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off:not(.dirty):not(.sticky)>.tab-actions, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.sticky-compact>.tab-actions { - display: none; - /* hide the tab actions when we are configured to hide it (unless dirty or sticky, but always when sticky-compact) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off:not(.dirty):not(.sticky) > .tab-actions, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.sticky-compact > .tab-actions { + display: none; /* hide the tab actions when we are configured to hide it (unless dirty or sticky, but always when sticky-compact) */ } -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.active>.tab-actions .action-label, -/* always show tab actions for active tab */ -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab>.tab-actions .action-label:focus, -/* always show tab actions on focus */ -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab:hover>.tab-actions .action-label, -/* always show tab actions on hover */ -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.active:hover>.tab-actions .action-label, -/* always show tab actions on hover */ -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.sticky>.tab-actions .action-label, -/* always show tab actions for sticky tabs */ -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.dirty>.tab-actions .action-label { - /* always show tab actions for dirty tabs */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active > .tab-actions .action-label, /* always show tab actions for active tab */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-actions .action-label:focus, /* always show tab actions on focus */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab:hover > .tab-actions .action-label, /* always show tab actions on hover */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.active:hover > .tab-actions .action-label, /* always show tab actions on hover */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky > .tab-actions .action-label, /* always show tab actions for sticky tabs */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label { /* always show tab actions for dirty tabs */ opacity: 1; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions .actions-container { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions .actions-container { justify-content: center; } -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab>.tab-actions .action-label.codicon { +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab > .tab-actions .action-label.codicon { color: inherit; font-size: 16px; padding: 2px; @@ -411,90 +378,80 @@ height: 16px; } -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.sticky.dirty>.tab-actions .action-label:not(:hover)::before, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky.dirty>.tab-actions .action-label:not(:hover)::before { - content: "\ebb2"; - /* use `pinned-dirty` icon unicode for sticky-dirty indication */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.sticky.dirty > .tab-actions .action-label:not(:hover)::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky.dirty > .tab-actions .action-label:not(:hover)::before { + content: "\ebb2"; /* use `pinned-dirty` icon unicode for sticky-dirty indication */ } -.monaco-workbench .part.editor>.content .editor-group-container.active>.title .tabs-container>.tab.dirty>.tab-actions .action-label:not(:hover)::before, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty>.tab-actions .action-label:not(:hover)::before { - content: "\ea71"; - /* use `circle-filled` icon unicode for dirty indication */ +.monaco-workbench .part.editor > .content .editor-group-container.active > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label:not(:hover)::before { + content: "\ea71"; /* use `circle-filled` icon unicode for dirty indication */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active>.tab-actions .action-label, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.active:hover>.tab-actions .action-label, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.dirty>.tab-actions .action-label, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sticky>.tab-actions .action-label, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab:hover>.tab-actions .action-label { - opacity: 0.5; - /* show tab actions dimmed for inactive group */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active > .tab-actions .action-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.active:hover > .tab-actions .action-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.dirty > .tab-actions .action-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sticky > .tab-actions .action-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab:hover > .tab-actions .action-label { + opacity: 0.5; /* show tab actions dimmed for inactive group */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab>.tab-actions .action-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions .action-label { opacity: 0; } /* Tab Actions: Off */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off { - padding-right: 10px; - /* give a little bit more room if tab actions is off */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off { + padding-right: 10px; /* give a little bit more room if tab actions is off */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-shrink.tab-actions-off:not(.sticky-compact), -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.sizing-fixed.tab-actions-off:not(.sticky-compact) { - padding-right: 5px; - /* we need less room when sizing is shrink/fixed (unless tab is sticky-compact) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink.tab-actions-off:not(.sticky-compact), +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed.tab-actions-off:not(.sticky-compact) { + padding-right: 5px; /* we need less room when sizing is shrink/fixed (unless tab is sticky-compact) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.dirty-border-top>.tab-actions { - display: none; - /* hide dirty state when highlightModifiedTabs is enabled and when running without tab actions */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty-border-top > .tab-actions { + display: none; /* hide dirty state when highlightModifiedTabs is enabled and when running without tab actions */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.dirty:not(.dirty-border-top):not(.sticky-compact), -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off.sticky:not(.sticky-compact) { - padding-right: 0; - /* remove extra padding when we are running without tab actions (unless tab is sticky-compact) */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.dirty:not(.dirty-border-top):not(.sticky-compact), +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off.sticky:not(.sticky-compact) { + padding-right: 0; /* remove extra padding when we are running without tab actions (unless tab is sticky-compact) */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab.tab-actions-off>.tab-actions { - pointer-events: none; - /* don't allow tab actions to be clicked when running without tab actions */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.tab-actions-off > .tab-actions { + pointer-events: none; /* don't allow tab actions to be clicked when running without tab actions */ } /* Breadcrumbs */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control { flex: 1 100%; height: 22px; cursor: default; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label { height: 22px; line-height: 22px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before { - height: 22px; - /* tweak the icon size of the editor labels when icons are enabled */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-icon-label::before { + height: 22px; /* tweak the icon size of the editor labels when icons are enabled */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .outline-element-icon { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .outline-element-icon { padding-right: 3px; - height: 22px; - /* tweak the icon size of the editor labels when icons are enabled */ + height: 22px; /* tweak the icon size of the editor labels when icons are enabled */ line-height: 22px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item { max-width: 80%; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before { width: 16px; height: 22px; display: flex; @@ -502,29 +459,28 @@ justify-content: center; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child { padding-right: 8px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child .codicon:last-child { - display: none; - /* hides chevrons when last item */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:last-child .codicon:last-child { + display: none; /* hides chevrons when last item */ } /* Editor Actions Toolbar */ -.monaco-workbench .part.editor>.content .editor-group-container>.title .editor-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions { cursor: default; flex: initial; padding: 0 8px 0 4px; height: var(--editor-group-title-height); } -.monaco-workbench .part.editor>.content .editor-group-container>.title .editor-actions .action-item { +.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions .action-item { margin-right: 4px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title>.tabs-and-actions-container.wrapping .editor-actions { +.monaco-workbench .part.editor > .content .editor-group-container > .title > .tabs-and-actions-container.wrapping .editor-actions { /* When tabs are wrapped, position the editor actions at the end of the very last row */ position: absolute; diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index ff27cffffb2..0b22793aacd 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -5,39 +5,37 @@ /* Editor Label */ -.monaco-workbench .part.editor>.content .editor-group-container>.title { +.monaco-workbench .part.editor > .content .editor-group-container > .title { cursor: pointer; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { white-space: nowrap; flex: 1; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label a, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label a { +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label a, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a { font-size: 13px; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .monaco-icon-label::before, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .monaco-icon-label::before, -.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label a, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label a, -.monaco-workbench .part.editor>.content .editor-group-container>.title .title-label h2, -.monaco-workbench .part.editor>.content .editor-group-container>.title .tabs-container>.tab .tab-label span { +.monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .monaco-icon-label::before, +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label a, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label a, +.monaco-workbench .part.editor > .content .editor-group-container > .title .title-label h2, +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label span { cursor: pointer; } -.monaco-workbench .part.editor>.content .editor-group-container>.title .monaco-icon-label::before { - height: var(--editor-group-title-height); - /* tweak the icon size of the editor labels when icons are enabled */ +.monaco-workbench .part.editor > .content .editor-group-container > .title .monaco-icon-label::before { + height: var(--editor-group-title-height); /* tweak the icon size of the editor labels when icons are enabled */ } -.monaco-workbench .part.editor>.content .editor-group-container>.title.breadcrumbs .monaco-icon-label::after, -.monaco-workbench .part.editor>.content .editor-group-container>.title.tabs .monaco-icon-label::after { - margin-right: 0; - /* by default the icon label has a padding right and this isn't wanted when not showing tabs and not showing breadcrumbs */ +.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .monaco-icon-label::after, +.monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .monaco-icon-label::after { + margin-right: 0; /* by default the icon label has a padding right and this isn't wanted when not showing tabs and not showing breadcrumbs */ } /* Drag and Drop */ From b506b0ce31fa74cb20063c0dc3c69a4bb1b24c72 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 6 Sep 2023 15:27:13 +0200 Subject: [PATCH 559/607] Fixes leaks --- .../test/browser/suggestWidgetModel.test.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 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 e46c38499f1..06cc564f3d9 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -35,8 +35,11 @@ import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions import { autorun } from 'vs/base/common/observable'; import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Suggest Widget Model', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + setup(() => { setUnexpectedErrorHandler(function (err) { throw err; @@ -136,7 +139,7 @@ async function withAsyncTestCodeEditorAndInlineCompletionsModel( const serviceCollection = new ServiceCollection( [ITelemetryService, NullTelemetryService], [ILogService, new NullLogService()], - [IStorageService, new InMemoryStorageService()], + [IStorageService, disposableStore.add(new InMemoryStorageService())], [IKeybindingService, new MockKeybindingService()], [IEditorWorkerService, new class extends mock() { override computeWordRanges() { @@ -166,8 +169,7 @@ async function withAsyncTestCodeEditorAndInlineCompletionsModel( if (options.provider) { const languageFeaturesService = new LanguageFeaturesService(); serviceCollection.set(ILanguageFeaturesService, languageFeaturesService); - const d = languageFeaturesService.completionProvider.register({ pattern: '**' }, options.provider); - disposableStore.add(d); + disposableStore.add(languageFeaturesService.completionProvider.register({ pattern: '**' }, options.provider)); } await withAsyncTestCodeEditor(text, { ...options, serviceCollection }, async (editor, editorViewModel, instantiationService) => { From 87f8a9fcfd70ff5d1ef7bb6521f11856ee65060f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 6 Sep 2023 15:34:22 +0200 Subject: [PATCH 560/607] Fixes leaking tests --- .../services/abstractCodeEditorService.ts | 7 ++++++- .../services/decorationRenderOptions.test.ts | 19 +++++++++++-------- .../common/model/modelInjectedText.test.ts | 18 ++++++------------ 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index cc0f8669a03..327a5828966 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -150,7 +150,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC this._editorStyleSheets.delete(editorId); } - public registerDecorationType(description: string, key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void { + public registerDecorationType(description: string, key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): IDisposable { let provider = this._decorationOptionProviders.get(key); if (!provider) { const styleSheet = this._getOrCreateStyleSheet(editor); @@ -169,6 +169,11 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC this._onDecorationTypeRegistered.fire(key); } provider.refCount++; + return { + dispose: () => { + this.removeDecorationType(key); + } + }; } public listDecorationTypes(): string[] { diff --git a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts index 68fa314066d..b8eb27e6d0f 100644 --- a/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts +++ b/src/vs/editor/test/browser/services/decorationRenderOptions.test.ts @@ -6,11 +6,14 @@ import * as assert from 'assert'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon'; import { TestCodeEditorService, TestGlobalStyleSheet } from 'vs/editor/test/browser/editorTestServices'; import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; suite('Decoration Render Options', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + const themeServiceMock = new TestThemeService(); const options: IDecorationRenderOptions = { @@ -20,12 +23,12 @@ suite('Decoration Render Options', () => { borderColor: 'yellow' }; test('register and resolve decoration type', () => { - const s = new TestCodeEditorService(themeServiceMock); - s.registerDecorationType('test', 'example', options); + const s = store.add(new TestCodeEditorService(themeServiceMock)); + store.add(s.registerDecorationType('test', 'example', options)); assert.notStrictEqual(s.resolveDecorationOptions('example', false), undefined); }); test('remove decoration type', () => { - const s = new TestCodeEditorService(themeServiceMock); + const s = store.add(new TestCodeEditorService(themeServiceMock)); s.registerDecorationType('test', 'example', options); assert.notStrictEqual(s.resolveDecorationOptions('example', false), undefined); s.removeDecorationType('example'); @@ -37,9 +40,9 @@ suite('Decoration Render Options', () => { } test('css properties', () => { - const s = new TestCodeEditorService(themeServiceMock); + const s = store.add(new TestCodeEditorService(themeServiceMock)); const styleSheet = s.globalStyleSheet; - s.registerDecorationType('test', 'example', options); + store.add(s.registerDecorationType('test', 'example', options)); const sheet = readStyleSheet(styleSheet); assert(sheet.indexOf(`{background:url('https://github.com/microsoft/vscode/blob/main/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0); assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0); @@ -54,7 +57,7 @@ suite('Decoration Render Options', () => { const themeService = new TestThemeService(new TestColorTheme({ editorBackground: '#FF0000' })); - const s = new TestCodeEditorService(themeService); + const s = store.add(new TestCodeEditorService(themeService)); const styleSheet = s.globalStyleSheet; s.registerDecorationType('test', 'example', options); assert.strictEqual(readStyleSheet(styleSheet), '.monaco-editor .ced-example-0 {background-color:#ff0000;border-color:transparent;box-sizing: border-box;}'); @@ -87,7 +90,7 @@ suite('Decoration Render Options', () => { editorBackground: '#FF0000', infoForeground: '#444444' })); - const s = new TestCodeEditorService(themeService); + const s = store.add(new TestCodeEditorService(themeService)); const styleSheet = s.globalStyleSheet; s.registerDecorationType('test', 'example', options); const expected = [ @@ -103,7 +106,7 @@ suite('Decoration Render Options', () => { }); test('css properties, gutterIconPaths', () => { - const s = new TestCodeEditorService(themeServiceMock); + const s = store.add(new TestCodeEditorService(themeServiceMock)); const styleSheet = s.globalStyleSheet; // URI, only minimal encoding diff --git a/src/vs/editor/test/common/model/modelInjectedText.test.ts b/src/vs/editor/test/common/model/modelInjectedText.test.ts index f31983f142f..72d0f9ba0a3 100644 --- a/src/vs/editor/test/common/model/modelInjectedText.test.ts +++ b/src/vs/editor/test/common/model/modelInjectedText.test.ts @@ -4,32 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; -import { TextModel } from 'vs/editor/common/model/textModel'; import { InternalModelContentChangeEvent, LineInjectedText, ModelRawChange, RawContentChangedType } from 'vs/editor/common/textModelEvents'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; suite('Editor Model - Injected Text Events', () => { - let thisModel: TextModel; - - setup(() => { - thisModel = createTextModel('First Line\nSecond Line'); - }); - - teardown(() => { - thisModel.dispose(); - }); + const store = ensureNoDisposablesAreLeakedInTestSuite(); test('Basic', () => { + const thisModel = store.add(createTextModel('First Line\nSecond Line')); + const recordedChanges = new Array(); - thisModel.onDidChangeContentOrInjectedText((e) => { + store.add(thisModel.onDidChangeContentOrInjectedText((e) => { const changes = (e instanceof InternalModelContentChangeEvent ? e.rawContentChangedEvent.changes : e.changes); for (const change of changes) { recordedChanges.push(mapChange(change)); } - }); + })); // Initial decoration let decorations = thisModel.deltaDecorations([], [{ From 4247015d33f8493628a7169a3ae2c8d96fa9d25b Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 6 Sep 2023 07:22:59 -0700 Subject: [PATCH 561/607] add `ensureNoDisposablesAreLeakedInTestSuite` and fix code (#192023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add `ensureNoDisposablesAreLeakedInTestSuite` and fix code ref https://github.com/microsoft/vscode/issues/190503 * more disposable findings * handle disposables in tests better * fix missing disposables --------- Co-authored-by: João Moreno --- src/vs/base/browser/ui/findinput/findInput.ts | 11 ++--- src/vs/base/browser/ui/list/listView.ts | 21 ++++---- .../browser/quickInputController.ts | 6 +-- .../quickinput/browser/quickInputList.ts | 2 + .../test/browser/quickinput.test.ts | 49 ++++++++----------- 5 files changed, 42 insertions(+), 47 deletions(-) diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index d663c086cff..8707c671aac 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -15,7 +15,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; import 'vs/css!./findInput'; import * as nls from 'vs/nls'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; export interface IFindInputOptions { @@ -50,7 +50,7 @@ export class FindInput extends Widget { private readonly showCommonFindToggles: boolean; private fixFocusOnOptionClickEnabled = true; private imeSessionInProgress = false; - private additionalTogglesDisposables: DisposableStore = new DisposableStore(); + private additionalTogglesDisposables: MutableDisposable = this._register(new MutableDisposable()); protected readonly controls: HTMLDivElement; protected readonly regex?: RegexToggle; @@ -278,14 +278,13 @@ export class FindInput extends Widget { currentToggle.domNode.remove(); } this.additionalToggles = []; - this.additionalTogglesDisposables.dispose(); - this.additionalTogglesDisposables = new DisposableStore(); + this.additionalTogglesDisposables.value = new DisposableStore(); for (const toggle of toggles ?? []) { - this.additionalTogglesDisposables.add(toggle); + this.additionalTogglesDisposables.value.add(toggle); this.controls.appendChild(toggle.domNode); - this.additionalTogglesDisposables.add(toggle.onChange(viaKeyboard => { + this.additionalTogglesDisposables.value.add(toggle.onChange(viaKeyboard => { this._onDidOptionChange.fire(viaKeyboard); if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) { this.inputBox.focus(); diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 769778f62f7..df252f81290 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -1525,20 +1525,21 @@ export class ListView implements IListView { // Dispose dispose() { - if (this.items) { - for (const item of this.items) { - if (item.row) { - const renderer = this.renderers.get(item.row.templateId); - if (renderer) { - renderer.disposeElement?.(item.element, -1, item.row.templateData, undefined); - renderer.disposeTemplate(item.row.templateData); - } + for (const item of this.items) { + item.dragStartDisposable.dispose(); + item.checkedDisposable.dispose(); + + if (item.row) { + const renderer = this.renderers.get(item.row.templateId); + if (renderer) { + renderer.disposeElement?.(item.element, -1, item.row.templateData, undefined); + renderer.disposeTemplate(item.row.templateData); } } - - this.items = []; } + this.items = []; + if (this.domNode && this.domNode.parentNode) { this.domNode.parentNode.removeChild(this.domNode); } diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index 932eec180e1..cd748881d21 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -124,14 +124,14 @@ export class QuickInputController extends Disposable { const count = new CountBadge(countContainer, { countFormat: localize({ key: 'quickInput.countSelected', comment: ['This tells the user how many items are selected in a list of items to select from. The items can be anything.'] }, "{0} Selected") }, this.styles.countBadge); const okContainer = dom.append(headerContainer, $('.quick-input-action')); - const ok = new Button(okContainer, this.styles.button); + const ok = this._register(new Button(okContainer, this.styles.button)); ok.label = localize('ok', "OK"); this._register(ok.onDidClick(e => { this.onDidAcceptEmitter.fire(); })); const customButtonContainer = dom.append(headerContainer, $('.quick-input-action')); - const customButton = new Button(customButtonContainer, this.styles.button); + const customButton = this._register(new Button(customButtonContainer, this.styles.button)); customButton.label = localize('custom', "Custom"); this._register(customButton.onDidClick(e => { this.onDidCustomEmitter.fire(); @@ -139,7 +139,7 @@ export class QuickInputController extends Disposable { const message = dom.append(inputContainer, $(`#${this.idPrefix}message.quick-input-message`)); - const progressBar = new ProgressBar(container, this.styles.progressBar); + const progressBar = this._register(new ProgressBar(container, this.styles.progressBar)); progressBar.getContainer().classList.add('quick-input-progress'); const widget = dom.append(container, $('.quick-input-html-widget')); diff --git a/src/vs/platform/quickinput/browser/quickInputList.ts b/src/vs/platform/quickinput/browser/quickInputList.ts index bb8dc4c1429..7512215aad9 100644 --- a/src/vs/platform/quickinput/browser/quickInputList.ts +++ b/src/vs/platform/quickinput/browser/quickInputList.ts @@ -268,6 +268,7 @@ class ListElementRenderer implements IListRendererdom.prepend(data.label.element, $('.quick-input-list-icon')); // Keybinding @@ -277,6 +278,7 @@ class ListElementRenderer implements IListRenderer { // https://github.com/microsoft/vscode/issues/147543 - let fixture: HTMLElement, controller: QuickInputController, quickpick: IQuickPick; - - function getScrollTop(): number { - return quickpick.scrollTop; - } + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let controller: QuickInputController; setup(() => { - fixture = document.createElement('div'); + const fixture = document.createElement('div'); document.body.appendChild(fixture); + store.add(toDisposable(() => document.body.removeChild(fixture))); - controller = new QuickInputController({ + controller = store.add(new QuickInputController({ container: fixture, idPrefix: 'testQuickInput', ignoreFocusOut() { return true; }, @@ -85,18 +84,12 @@ suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 } } }, - new TestThemeService()); + new TestThemeService())); // initial layout controller.layout({ height: 20, width: 40 }, 0); }); - teardown(() => { - quickpick?.dispose(); - controller.dispose(); - document.body.removeChild(fixture); - }); - test('pick - basecase', async () => { const item = { label: 'foo' }; @@ -135,10 +128,10 @@ suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 }); test('onDidChangeValue - gets triggered when .value is set', async () => { - quickpick = controller.createQuickPick(); + const quickpick = store.add(controller.createQuickPick()); let value: string | undefined = undefined; - quickpick.onDidChangeValue((e) => value = e); + store.add(quickpick.onDidChangeValue((e) => value = e)); // Trigger a change quickpick.value = 'changed'; @@ -151,7 +144,7 @@ suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 }); test('keepScrollPosition - works with activeItems', async () => { - quickpick = controller.createQuickPick(); + const quickpick = store.add(controller.createQuickPick()); const items = []; for (let i = 0; i < 1000; i++) { @@ -162,21 +155,21 @@ suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 quickpick.activeItems = [items[items.length - 1]]; quickpick.show(); - const cursorTop = getScrollTop(); + const cursorTop = quickpick.scrollTop; assert.notStrictEqual(cursorTop, 0); quickpick.keepScrollPosition = true; quickpick.activeItems = [items[0]]; - assert.strictEqual(cursorTop, getScrollTop()); + assert.strictEqual(cursorTop, quickpick.scrollTop); quickpick.keepScrollPosition = false; quickpick.activeItems = [items[0]]; - assert.strictEqual(getScrollTop(), 0); + assert.strictEqual(quickpick.scrollTop, 0); }); test('keepScrollPosition - works with items', async () => { - quickpick = controller.createQuickPick(); + const quickpick = store.add(controller.createQuickPick()); const items = []; for (let i = 0; i < 1000; i++) { @@ -187,29 +180,29 @@ suite('QuickInput', () => { // https://github.com/microsoft/vscode/issues/147543 quickpick.activeItems = [items[items.length - 1]]; quickpick.show(); - const cursorTop = getScrollTop(); + const cursorTop = quickpick.scrollTop; assert.notStrictEqual(cursorTop, 0); quickpick.keepScrollPosition = true; quickpick.items = items; - assert.strictEqual(cursorTop, getScrollTop()); + assert.strictEqual(cursorTop, quickpick.scrollTop); quickpick.keepScrollPosition = false; quickpick.items = items; - assert.strictEqual(getScrollTop(), 0); + assert.strictEqual(quickpick.scrollTop, 0); }); test('selectedItems - verify previous selectedItems does not hang over to next set of items', async () => { - quickpick = controller.createQuickPick(); + const quickpick = store.add(controller.createQuickPick()); quickpick.items = [{ label: 'step 1' }]; quickpick.show(); void (await new Promise(resolve => { - quickpick.onDidAccept(() => { + store.add(quickpick.onDidAccept(() => { quickpick.canSelectMany = true; quickpick.items = [{ label: 'a' }, { label: 'b' }, { label: 'c' }]; resolve(); - }); + })); // accept 'step 1' controller.accept(); From 3788d799d249081b646e9dc76b94919ad7d39f2b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 6 Sep 2023 16:30:59 +0200 Subject: [PATCH 562/607] Git - improve logging for git detection (#192297) --- extensions/git/src/git.ts | 20 +++++++++++--------- extensions/git/src/main.ts | 2 +- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 36dbac3a56e..502e8de947b 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -66,14 +66,14 @@ function parseVersion(raw: string): string { function findSpecificGit(path: string, onValidate: (path: string) => boolean): Promise { return new Promise((c, e) => { if (!onValidate(path)) { - return e('git not found'); + return e(new Error(`Path "${path}" is invalid.`)); } const buffers: Buffer[] = []; const child = cp.spawn(path, ['--version']); child.stdout.on('data', (b: Buffer) => buffers.push(b)); child.on('error', cpErrorHandler(e)); - child.on('close', code => code ? e(new Error('Not found')) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) })); + child.on('close', code => code ? e(new Error(`Not found. Code: ${code}`)) : c({ path, version: parseVersion(Buffer.concat(buffers).toString('utf8').trim()) })); }); } @@ -81,21 +81,21 @@ function findGitDarwin(onValidate: (path: string) => boolean): Promise { return new Promise((c, e) => { cp.exec('which git', (err, gitPathBuffer) => { if (err) { - return e('git not found'); + return e(new Error(`Executing "which git" failed: ${err.message}`)); } const path = gitPathBuffer.toString().trim(); function getVersion(path: string) { if (!onValidate(path)) { - return e('git not found'); + return e(new Error(`Path "${path}" is invalid.`)); } // make sure git executes cp.exec('git --version', (err, stdout) => { if (err) { - return e('git not found'); + return e(new Error(`Executing "git --version" failed: ${err.message}`)); } return c({ path, version: parseVersion(stdout.trim()) }); @@ -112,7 +112,7 @@ function findGitDarwin(onValidate: (path: string) => boolean): Promise { // git is not installed, and launching /usr/bin/git // will prompt the user to install it - return e('git not found'); + return e(new Error('Executing "xcode-select -p" failed with error code 2.')); } getVersion(path); @@ -142,12 +142,13 @@ function findGitWin32(onValidate: (path: string) => boolean): Promise { .then(undefined, () => findGitWin32InPath(onValidate)); } -export async function findGit(hints: string[], onValidate: (path: string) => boolean): Promise { +export async function findGit(hints: string[], onValidate: (path: string) => boolean, logger: LogOutputChannel): Promise { for (const hint of hints) { try { return await findSpecificGit(hint, onValidate); - } catch { + } catch (err) { // noop + logger.info(`Unable to find git on the PATH: "${hint}". Error: ${err.message}`); } } @@ -157,8 +158,9 @@ export async function findGit(hints: string[], onValidate: (path: string) => boo case 'win32': return await findGitWin32(onValidate); default: return await findSpecificGit('git', onValidate); } - } catch { + } catch (err) { // noop + logger.warn(`Unable to find git. Error: ${err.message}`); } throw new Error('Git installation not found.'); diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 7c93979ee69..5440795cce9 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -58,7 +58,7 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, logger.info(l10n.t('Skipped found git in: "{0}"', gitPath)); } return !skip; - }); + }, logger); let ipcServer: IPCServer | undefined = undefined; From 82af26f7068754312f5454175c45f8648a45eac1 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 6 Sep 2023 16:20:51 +0200 Subject: [PATCH 563/607] Cleans up diff editor. Fixes #185944 --- .../browser/widget/codeEditorContributions.ts | 1 - .../browser/widget/diffEditor.contribution.ts | 124 ---------- .../accessibleDiffViewer.css} | 0 .../accessibleDiffViewer.ts | 5 +- .../colors.ts | 0 .../decorations.ts | 0 .../delegatingEditorImpl.ts | 0 .../diffEditor.contribution.ts} | 142 +++++++++-- .../diffEditorDecorations.ts | 12 +- .../diffEditorEditors.ts | 4 +- .../diffEditorOptions.ts | 3 +- .../diffEditorSash.ts | 0 .../diffEditorViewModel.ts | 4 +- .../diffEditorWidget.ts} | 19 +- .../diffProviderFactoryService.ts | 2 +- .../hideUnchangedRegionsFeature.ts | 10 +- .../inlineDiffDeletedCodeMargin.ts | 4 +- .../lineAlignment.ts | 16 +- .../movedBlocksLines.ts | 6 +- .../outlineModel.ts | 0 .../overviewRulerPart.ts | 6 +- .../renderLines.ts | 0 .../diffEditor.css => diffEditor/style.css} | 148 +++++++++++ .../utils.ts | 0 .../workerBasedDocumentDiffProvider.ts | 0 .../widget/diffEditorWidget2/style.css | 151 ------------ .../widget/embeddedCodeEditorWidget.ts | 7 +- .../editor/browser/widget/inlineDiffMargin.ts | 231 ------------------ src/vs/editor/editor.all.ts | 2 +- .../browser/standaloneCodeEditor.ts | 4 +- .../browser/widget/diffEditorWidget2.test.ts | 2 +- .../parts/editor/editor.contribution.ts | 1 - .../browser/parts/editor/textDiffEditor.ts | 6 +- .../browser/actions/chatAccessibilityHelp.ts | 2 +- .../codeEditor/browser/diffEditorHelper.ts | 21 +- .../inlineChat/browser/inlineChatActions.ts | 4 +- .../browser/inlineChatLivePreviewWidget.ts | 4 +- .../inlineChat/browser/inlineChatWidget.ts | 6 +- .../notebook/browser/diff/diffComponents.ts | 16 +- .../browser/diff/diffElementViewModel.ts | 6 +- .../browser/diff/notebookDiffEditorBrowser.ts | 4 +- .../notebook/browser/diff/notebookDiffList.ts | 4 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 8 +- .../testing/browser/testingOutputPeek.ts | 12 +- 44 files changed, 367 insertions(+), 630 deletions(-) delete mode 100644 src/vs/editor/browser/widget/diffEditor.contribution.ts rename src/vs/editor/browser/widget/{media/diffReview.css => diffEditor/accessibleDiffViewer.css} (100%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/accessibleDiffViewer.ts (99%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/colors.ts (100%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/decorations.ts (100%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/delegatingEditorImpl.ts (100%) rename src/vs/editor/browser/widget/{diffEditorWidget2/diffEditorWidget2.contribution.ts => diffEditor/diffEditor.contribution.ts} (60%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/diffEditorDecorations.ts (96%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/diffEditorEditors.ts (99%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/diffEditorOptions.ts (98%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/diffEditorSash.ts (100%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/diffEditorViewModel.ts (99%) rename src/vs/editor/browser/widget/{diffEditorWidget2/diffEditorWidget2.ts => diffEditor/diffEditorWidget.ts} (98%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/diffProviderFactoryService.ts (96%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/hideUnchangedRegionsFeature.ts (98%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/inlineDiffDeletedCodeMargin.ts (98%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/lineAlignment.ts (98%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/movedBlocksLines.ts (98%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/outlineModel.ts (100%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/overviewRulerPart.ts (98%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/renderLines.ts (100%) rename src/vs/editor/browser/widget/{media/diffEditor.css => diffEditor/style.css} (57%) rename src/vs/editor/browser/widget/{diffEditorWidget2 => diffEditor}/utils.ts (100%) rename src/vs/editor/browser/widget/{ => diffEditor}/workerBasedDocumentDiffProvider.ts (100%) delete mode 100644 src/vs/editor/browser/widget/diffEditorWidget2/style.css delete mode 100644 src/vs/editor/browser/widget/inlineDiffMargin.ts diff --git a/src/vs/editor/browser/widget/codeEditorContributions.ts b/src/vs/editor/browser/widget/codeEditorContributions.ts index d9570b7ced9..35bf94643b2 100644 --- a/src/vs/editor/browser/widget/codeEditorContributions.ts +++ b/src/vs/editor/browser/widget/codeEditorContributions.ts @@ -10,7 +10,6 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorContributionInstantiation, IEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import './diffEditor.contribution'; export class CodeEditorContributions extends Disposable { diff --git a/src/vs/editor/browser/widget/diffEditor.contribution.ts b/src/vs/editor/browser/widget/diffEditor.contribution.ts deleted file mode 100644 index 9506f2bd5a7..00000000000 --- a/src/vs/editor/browser/widget/diffEditor.contribution.ts +++ /dev/null @@ -1,124 +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 { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { localize } from 'vs/nls'; -import { ILocalizedString } from 'vs/platform/action/common/action'; -import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; - -const accessibleDiffViewerCategory: ILocalizedString = { - value: localize('accessibleDiffViewer', 'Accessible Diff Viewer'), - original: 'Accessible Diff Viewer', -}; - -export class AccessibleDiffViewerNext extends Action2 { - public static id = 'editor.action.accessibleDiffViewer.next'; - - constructor() { - super({ - id: AccessibleDiffViewerNext.id, - title: { value: localize('editor.action.accessibleDiffViewer.next', "Go to Next Difference"), original: 'Go to Next Difference' }, - category: accessibleDiffViewerCategory, - precondition: ContextKeyExpr.has('isInDiffEditor'), - keybinding: { - primary: KeyCode.F7, - weight: KeybindingWeight.EditorContrib - }, - f1: true, - }); - } - - public override run(accessor: ServicesAccessor): void { - const diffEditor = findFocusedDiffEditor(accessor); - diffEditor?.accessibleDiffViewerNext(); - } -} - -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { - command: { - id: AccessibleDiffViewerNext.id, - title: localize('Open Accessible Diff Viewer', "Open Accessible Diff Viewer"), - }, - order: 10, - group: '2_diff', - when: ContextKeyExpr.and( - EditorContextKeys.accessibleDiffViewerVisible.negate(), - ContextKeyExpr.has('isInDiffEditor'), - ), -}); - -export class AccessibleDiffViewerPrev extends Action2 { - public static id = 'editor.action.accessibleDiffViewer.prev'; - - constructor() { - super({ - id: AccessibleDiffViewerPrev.id, - title: { value: localize('editor.action.accessibleDiffViewer.prev', "Go to Previous Difference"), original: 'Go to Previous Difference' }, - category: accessibleDiffViewerCategory, - precondition: ContextKeyExpr.has('isInDiffEditor'), - keybinding: { - primary: KeyMod.Shift | KeyCode.F7, - weight: KeybindingWeight.EditorContrib - }, - f1: true, - }); - } - - public override run(accessor: ServicesAccessor): void { - const diffEditor = findFocusedDiffEditor(accessor); - diffEditor?.accessibleDiffViewerPrev(); - } -} - -export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null { - const codeEditorService = accessor.get(ICodeEditorService); - const diffEditors = codeEditorService.listDiffEditors(); - const activeCodeEditor = codeEditorService.getFocusedCodeEditor() ?? codeEditorService.getActiveCodeEditor(); - if (!activeCodeEditor) { - return null; - } - - for (let i = 0, len = diffEditors.length; i < len; i++) { - const diffEditor = diffEditors[i]; - if (diffEditor.getModifiedEditor().getId() === activeCodeEditor.getId() || diffEditor.getOriginalEditor().getId() === activeCodeEditor.getId()) { - return diffEditor; - } - } - - if (document.activeElement) { - for (const d of diffEditors) { - const container = d.getContainerDomNode(); - if (isElementOrParentOf(container, document.activeElement)) { - return d; - } - } - } - - return null; -} - -function isElementOrParentOf(elementOrParent: Element, element: Element): boolean { - let e: Element | null = element; - while (e) { - if (e === elementOrParent) { - return true; - } - e = e.parentElement; - } - return false; -} - -CommandsRegistry.registerCommandAlias('editor.action.diffReview.next', AccessibleDiffViewerNext.id); -registerAction2(AccessibleDiffViewerNext); - -CommandsRegistry.registerCommandAlias('editor.action.diffReview.prev', AccessibleDiffViewerPrev.id); -registerAction2(AccessibleDiffViewerPrev); diff --git a/src/vs/editor/browser/widget/media/diffReview.css b/src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.css similarity index 100% rename from src/vs/editor/browser/widget/media/diffReview.css rename to src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.css diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.ts similarity index 99% rename from src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts rename to src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.ts index 3b2f9b3204a..e7a8d7c5181 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditor/accessibleDiffViewer.ts @@ -15,8 +15,8 @@ import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecy import { IObservable, ITransaction, autorun, autorunWithStore, derived, derivedWithStore, recomputeInitiallyAndOnChange, observableValue, subtransaction, transaction } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; -import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; -import { applyStyle } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/diffEditorEditors'; +import { applyStyle } from 'vs/editor/browser/widget/diffEditor/utils'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; @@ -33,6 +33,7 @@ import { localize } from 'vs/nls'; import { AudioCue, IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; +import 'vs/css!./accessibleDiffViewer'; const accessibleDiffViewerInsertIcon = registerIcon('diff-review-insert', Codicon.add, localize('accessibleDiffViewerInsertIcon', 'Icon for \'Insert\' in accessible diff viewer.')); const accessibleDiffViewerRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, localize('accessibleDiffViewerRemoveIcon', 'Icon for \'Remove\' in accessible diff viewer.')); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/colors.ts b/src/vs/editor/browser/widget/diffEditor/colors.ts similarity index 100% rename from src/vs/editor/browser/widget/diffEditorWidget2/colors.ts rename to src/vs/editor/browser/widget/diffEditor/colors.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/decorations.ts b/src/vs/editor/browser/widget/diffEditor/decorations.ts similarity index 100% rename from src/vs/editor/browser/widget/diffEditorWidget2/decorations.ts rename to src/vs/editor/browser/widget/diffEditor/decorations.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/delegatingEditorImpl.ts b/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts similarity index 100% rename from src/vs/editor/browser/widget/diffEditorWidget2/delegatingEditorImpl.ts rename to src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts b/src/vs/editor/browser/widget/diffEditor/diffEditor.contribution.ts similarity index 60% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts rename to src/vs/editor/browser/widget/diffEditor/diffEditor.contribution.ts index 79ef25a3e44..7eb51c59d65 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditor.contribution.ts @@ -4,17 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { Codicon } from 'vs/base/common/codicons'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; -import { findFocusedDiffEditor } from 'vs/editor/browser/widget/diffEditor.contribution'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize } from 'vs/nls'; import { ILocalizedString } from 'vs/platform/action/common/action'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; export class ToggleCollapseUnchangedRegions extends Action2 { constructor() { @@ -22,13 +24,11 @@ export class ToggleCollapseUnchangedRegions extends Action2 { id: 'diffEditor.toggleCollapseUnchangedRegions', title: { value: localize('toggleCollapseUnchangedRegions', "Toggle Collapse Unchanged Regions"), original: 'Toggle Collapse Unchanged Regions' }, icon: Codicon.map, - precondition: ContextKeyEqualsExpr.create('diffEditorVersion', 2), toggled: ContextKeyExpr.has('config.diffEditor.hideUnchangedRegions.enabled'), menu: { id: MenuId.EditorTitle, order: 22, group: 'navigation', - when: ContextKeyEqualsExpr.create('diffEditorVersion', 2), }, }); } @@ -47,7 +47,6 @@ export class ToggleShowMovedCodeBlocks extends Action2 { super({ id: 'diffEditor.toggleShowMovedCodeBlocks', title: { value: localize('toggleShowMovedCodeBlocks', "Toggle Show Moved Code Blocks"), original: 'Toggle Show Moved Code Blocks' }, - precondition: ContextKeyEqualsExpr.create('diffEditorVersion', 2), }); } @@ -65,7 +64,6 @@ export class ToggleUseInlineViewWhenSpaceIsLimited extends Action2 { super({ id: 'diffEditor.toggleUseInlineViewWhenSpaceIsLimited', title: { value: localize('toggleUseInlineViewWhenSpaceIsLimited', "Toggle Use Inline View When Space Is Limited"), original: 'Toggle Use Inline View When Space Is Limited' }, - precondition: ContextKeyEqualsExpr.create('diffEditorVersion', 2), }); } @@ -86,10 +84,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { }, order: 11, group: '1_diff', - when: ContextKeyExpr.and( - EditorContextKeys.diffEditorRenderSideBySideInlineBreakpointReached, - ContextKeyEqualsExpr.create('diffEditorVersion', 2) - ) + when: EditorContextKeys.diffEditorRenderSideBySideInlineBreakpointReached, }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { @@ -101,7 +96,6 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { }, order: 10, group: '1_diff', - when: ContextKeyEqualsExpr.create('diffEditorVersion', 2) }); const diffEditorCategory: ILocalizedString = { @@ -115,7 +109,7 @@ export class SwitchSide extends EditorAction2 { id: 'diffEditor.switchSide', title: { value: localize('switchSide', "Switch Side"), original: 'Switch Side' }, icon: Codicon.arrowSwap, - precondition: ContextKeyExpr.and(ContextKeyEqualsExpr.create('diffEditorVersion', 2), ContextKeyExpr.has('isInDiffEditor')), + precondition: ContextKeyExpr.has('isInDiffEditor'), f1: true, category: diffEditorCategory, }); @@ -123,7 +117,7 @@ export class SwitchSide extends EditorAction2 { runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, arg?: { dryRun: boolean }): unknown { const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget2) { + if (diffEditor instanceof DiffEditorWidget) { if (arg && arg.dryRun) { return { destinationSelection: diffEditor.mapToOtherSide().destinationSelection }; } else { @@ -154,7 +148,7 @@ export class ExitCompareMove extends EditorAction2 { runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget2) { + if (diffEditor instanceof DiffEditorWidget) { diffEditor.exitCompareMove(); } } @@ -168,7 +162,7 @@ export class CollapseAllUnchangedRegions extends EditorAction2 { id: 'diffEditor.collapseAllUnchangedRegions', title: { value: localize('collapseAllUnchangedRegions', "Collapse All Unchanged Regions"), original: 'Collapse All Unchanged Regions' }, icon: Codicon.fold, - precondition: ContextKeyExpr.and(ContextKeyEqualsExpr.create('diffEditorVersion', 2), ContextKeyExpr.has('isInDiffEditor')), + precondition: ContextKeyExpr.has('isInDiffEditor'), f1: true, category: diffEditorCategory, }); @@ -176,7 +170,7 @@ export class CollapseAllUnchangedRegions extends EditorAction2 { runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget2) { + if (diffEditor instanceof DiffEditorWidget) { diffEditor.collapseAllUnchangedRegions(); } } @@ -190,7 +184,7 @@ export class ShowAllUnchangedRegions extends EditorAction2 { id: 'diffEditor.showAllUnchangedRegions', title: { value: localize('showAllUnchangedRegions', "Show All Unchanged Regions"), original: 'Show All Unchanged Regions' }, icon: Codicon.unfold, - precondition: ContextKeyExpr.and(ContextKeyEqualsExpr.create('diffEditorVersion', 2), ContextKeyExpr.has('isInDiffEditor')), + precondition: ContextKeyExpr.has('isInDiffEditor'), f1: true, category: diffEditorCategory, }); @@ -198,10 +192,118 @@ export class ShowAllUnchangedRegions extends EditorAction2 { runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, ...args: unknown[]): void { const diffEditor = findFocusedDiffEditor(accessor); - if (diffEditor instanceof DiffEditorWidget2) { + if (diffEditor instanceof DiffEditorWidget) { diffEditor.showAllUnchangedRegions(); } } } registerAction2(ShowAllUnchangedRegions); + +const accessibleDiffViewerCategory: ILocalizedString = { + value: localize('accessibleDiffViewer', 'Accessible Diff Viewer'), + original: 'Accessible Diff Viewer', +}; + +export class AccessibleDiffViewerNext extends Action2 { + public static id = 'editor.action.accessibleDiffViewer.next'; + + constructor() { + super({ + id: AccessibleDiffViewerNext.id, + title: { value: localize('editor.action.accessibleDiffViewer.next', "Go to Next Difference"), original: 'Go to Next Difference' }, + category: accessibleDiffViewerCategory, + precondition: ContextKeyExpr.has('isInDiffEditor'), + keybinding: { + primary: KeyCode.F7, + weight: KeybindingWeight.EditorContrib + }, + f1: true, + }); + } + + public override run(accessor: ServicesAccessor): void { + const diffEditor = findFocusedDiffEditor(accessor); + diffEditor?.accessibleDiffViewerNext(); + } +} + +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: AccessibleDiffViewerNext.id, + title: localize('Open Accessible Diff Viewer', "Open Accessible Diff Viewer"), + }, + order: 10, + group: '2_diff', + when: ContextKeyExpr.and( + EditorContextKeys.accessibleDiffViewerVisible.negate(), + ContextKeyExpr.has('isInDiffEditor'), + ), +}); + +export class AccessibleDiffViewerPrev extends Action2 { + public static id = 'editor.action.accessibleDiffViewer.prev'; + + constructor() { + super({ + id: AccessibleDiffViewerPrev.id, + title: { value: localize('editor.action.accessibleDiffViewer.prev', "Go to Previous Difference"), original: 'Go to Previous Difference' }, + category: accessibleDiffViewerCategory, + precondition: ContextKeyExpr.has('isInDiffEditor'), + keybinding: { + primary: KeyMod.Shift | KeyCode.F7, + weight: KeybindingWeight.EditorContrib + }, + f1: true, + }); + } + + public override run(accessor: ServicesAccessor): void { + const diffEditor = findFocusedDiffEditor(accessor); + diffEditor?.accessibleDiffViewerPrev(); + } +} + +export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | null { + const codeEditorService = accessor.get(ICodeEditorService); + const diffEditors = codeEditorService.listDiffEditors(); + const activeCodeEditor = codeEditorService.getFocusedCodeEditor() ?? codeEditorService.getActiveCodeEditor(); + if (!activeCodeEditor) { + return null; + } + + for (let i = 0, len = diffEditors.length; i < len; i++) { + const diffEditor = diffEditors[i]; + if (diffEditor.getModifiedEditor().getId() === activeCodeEditor.getId() || diffEditor.getOriginalEditor().getId() === activeCodeEditor.getId()) { + return diffEditor; + } + } + + if (document.activeElement) { + for (const d of diffEditors) { + const container = d.getContainerDomNode(); + if (isElementOrParentOf(container, document.activeElement)) { + return d; + } + } + } + + return null; +} + +function isElementOrParentOf(elementOrParent: Element, element: Element): boolean { + let e: Element | null = element; + while (e) { + if (e === elementOrParent) { + return true; + } + e = e.parentElement; + } + return false; +} + +CommandsRegistry.registerCommandAlias('editor.action.diffReview.next', AccessibleDiffViewerNext.id); +registerAction2(AccessibleDiffViewerNext); + +CommandsRegistry.registerCommandAlias('editor.action.diffReview.prev', AccessibleDiffViewerPrev.id); +registerAction2(AccessibleDiffViewerPrev); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorDecorations.ts similarity index 96% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts rename to src/vs/editor/browser/widget/diffEditor/diffEditorDecorations.ts index bdc8100df4a..2e8fc3f0cf3 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorDecorations.ts @@ -5,12 +5,12 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, derived } from 'vs/base/common/observable'; -import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditorWidget2/decorations'; -import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; -import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions'; -import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; -import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines'; -import { applyObservableDecorations } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { arrowRevertChange, diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditor/decorations'; +import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/diffEditorEditors'; +import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorOptions'; +import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; +import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditor/movedBlocksLines'; +import { applyObservableDecorations } from 'vs/editor/browser/widget/diffEditor/utils'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IModelDeltaDecoration } from 'vs/editor/common/model'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorEditors.ts similarity index 99% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts rename to src/vs/editor/browser/widget/diffEditor/diffEditorEditors.ts index 1166c4522b8..bb6cbeef5c5 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorEditors.ts @@ -8,7 +8,7 @@ import { IObservable, IReader, autorunHandleChanges, observableFromEvent } from import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart'; +import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditor/overviewRulerPart'; import { EditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IContentSizeChangedEvent } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; @@ -16,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { DiffEditorOptions } from './diffEditorOptions'; import { ITextModel } from 'vs/editor/common/model'; -import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; export class DiffEditorEditors extends Disposable { public readonly modified: CodeEditorWidget; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts similarity index 98% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts rename to src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts index ed0de92dad0..7126405bf34 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts @@ -20,7 +20,8 @@ export class DiffEditorOptions { this._options = observableValue(this, optionsCopy); } - public readonly couldShowInlineViewBecauseOfSize = derived(this, reader => this._options.read(reader).renderSideBySide && this.diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint + public readonly couldShowInlineViewBecauseOfSize = derived(this, reader => + this._options.read(reader).renderSideBySide && this.diffEditorWidth.read(reader) <= this._options.read(reader).renderSideBySideInlineBreakpoint ); public readonly renderOverviewRuler = derived(this, reader => this._options.read(reader).renderOverviewRuler); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorSash.ts similarity index 100% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffEditorSash.ts rename to src/vs/editor/browser/widget/diffEditor/diffEditorSash.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts similarity index 99% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts rename to src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index da024fec498..28914cc7421 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -8,8 +8,8 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, ITransaction, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService'; -import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; +import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; import { ISerializedLineRange, LineRange } from 'vs/editor/common/core/lineRange'; import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { IDocumentDiff } from 'vs/editor/common/diff/documentDiffProvider'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts similarity index 98% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts rename to src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 0e169daeb7b..ee4e12ee881 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -15,14 +15,14 @@ import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions, IMouseTargetV import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { AccessibleDiffViewer } from 'vs/editor/browser/widget/diffEditorWidget2/accessibleDiffViewer'; -import { DiffEditorDecorations } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorDecorations'; -import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorSash'; -import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature'; -import { ViewZoneManager } from 'vs/editor/browser/widget/diffEditorWidget2/lineAlignment'; -import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines'; -import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart'; -import { CSSStyle, ObservableElementSizeObserver, applyStyle, readHotReloadableExport } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { AccessibleDiffViewer } from 'vs/editor/browser/widget/diffEditor/accessibleDiffViewer'; +import { DiffEditorDecorations } from 'vs/editor/browser/widget/diffEditor/diffEditorDecorations'; +import { DiffEditorSash } from 'vs/editor/browser/widget/diffEditor/diffEditorSash'; +import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature'; +import { ViewZoneManager } from 'vs/editor/browser/widget/diffEditor/lineAlignment'; +import { MovedBlocksLinesPart } from 'vs/editor/browser/widget/diffEditor/movedBlocksLines'; +import { OverviewRulerPart } from 'vs/editor/browser/widget/diffEditor/overviewRulerPart'; +import { CSSStyle, ObservableElementSizeObserver, applyStyle, readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IDimension } from 'vs/editor/common/core/dimension'; import { Position } from 'vs/editor/common/core/position'; @@ -50,7 +50,7 @@ export interface IDiffCodeEditorWidgetOptions { modifiedEditor?: ICodeEditorWidgetOptions; } -export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { +export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { public static ENTIRE_DIFF_OVERVIEW_WIDTH = OverviewRulerPart.ENTIRE_DIFF_OVERVIEW_WIDTH; private readonly elements = h('div.monaco-diff-editor.side-by-side', { style: { position: 'relative', height: '100%' } }, [ @@ -103,7 +103,6 @@ export class DiffEditorWidget2 extends DelegatingEditor implements IDiffEditor { codeEditorService.willCreateDiffEditor(); this._contextKeyService.createKey('isInDiffEditor', true); - this._contextKeyService.createKey('diffEditorVersion', 2); this._domElement.appendChild(this.elements.root); this._register(toDisposable(() => this._domElement.removeChild(this.elements.root))); diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService.ts b/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts similarity index 96% rename from src/vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService.ts rename to src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts index fd7bb19ac4b..3fe7ebb61d4 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/diffProviderFactoryService.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/workerBasedDocumentDiffProvider'; +import { WorkerBasedDocumentDiffProvider } from 'vs/editor/browser/widget/diffEditor/workerBasedDocumentDiffProvider'; import { IDocumentDiffProvider } from 'vs/editor/common/diff/documentDiffProvider'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature.ts similarity index 98% rename from src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts rename to src/vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature.ts index 5beb8c3ff37..2017672847b 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/hideUnchangedRegionsFeature.ts @@ -14,11 +14,11 @@ import { IObservable, IReader, autorun, autorunWithStore, derived, derivedWithSt import { ThemeIcon } from 'vs/base/common/themables'; import { isDefined } from 'vs/base/common/types'; import { ICodeEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; -import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; -import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorOptions'; -import { DiffEditorViewModel, UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; -import { OutlineModel } from 'vs/editor/browser/widget/diffEditorWidget2/outlineModel'; -import { DisposableCancellationTokenSource, PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/diffEditorEditors'; +import { DiffEditorOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorOptions'; +import { DiffEditorViewModel, UnchangedRegion } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; +import { OutlineModel } from 'vs/editor/browser/widget/diffEditor/outlineModel'; +import { DisposableCancellationTokenSource, PlaceholderViewZone, ViewZoneOverlayWidget, applyObservableDecorations, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditor/utils'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts b/src/vs/editor/browser/widget/diffEditor/inlineDiffDeletedCodeMargin.ts similarity index 98% rename from src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts rename to src/vs/editor/browser/widget/diffEditor/inlineDiffDeletedCodeMargin.ts index f5b3ff67e26..fb70a7ac982 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin.ts +++ b/src/vs/editor/browser/widget/diffEditor/inlineDiffDeletedCodeMargin.ts @@ -11,7 +11,7 @@ import { isIOS } from 'vs/base/common/platform'; import { ThemeIcon } from 'vs/base/common/themables'; import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; @@ -40,7 +40,7 @@ export class InlineDiffDeletedCodeMargin extends Disposable { private readonly _marginDomNode: HTMLElement, private readonly _modifiedEditor: CodeEditorWidget, private readonly _diff: DetailedLineRangeMapping, - private readonly _editor: DiffEditorWidget2, + private readonly _editor: DiffEditorWidget, private readonly _viewLineCounts: number[], private readonly _originalTextModel: ITextModel, private readonly _contextMenuService: IContextMenuService, diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts b/src/vs/editor/browser/widget/diffEditor/lineAlignment.ts similarity index 98% rename from src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts rename to src/vs/editor/browser/widget/diffEditor/lineAlignment.ts index 86514a3a1ad..02502e99971 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/lineAlignment.ts +++ b/src/vs/editor/browser/widget/diffEditor/lineAlignment.ts @@ -15,13 +15,13 @@ import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { IViewZone } from 'vs/editor/browser/editorBrowser'; import { StableEditorScrollState } from 'vs/editor/browser/stableEditorScroll'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { diffDeleteDecoration, diffRemoveIcon } from 'vs/editor/browser/widget/diffEditorWidget2/decorations'; -import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; -import { DiffEditorViewModel, DiffMapping } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; -import { InlineDiffDeletedCodeMargin } from 'vs/editor/browser/widget/diffEditorWidget2/inlineDiffDeletedCodeMargin'; -import { LineSource, RenderOptions, renderLines } from 'vs/editor/browser/widget/diffEditorWidget2/renderLines'; -import { animatedObservable, joinCombine } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { diffDeleteDecoration, diffRemoveIcon } from 'vs/editor/browser/widget/diffEditor/decorations'; +import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/diffEditorEditors'; +import { DiffEditorViewModel, DiffMapping } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { InlineDiffDeletedCodeMargin } from 'vs/editor/browser/widget/diffEditor/inlineDiffDeletedCodeMargin'; +import { LineSource, RenderOptions, renderLines } from 'vs/editor/browser/widget/diffEditor/renderLines'; +import { animatedObservable, joinCombine } from 'vs/editor/browser/widget/diffEditor/utils'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; @@ -53,7 +53,7 @@ export class ViewZoneManager extends Disposable { private readonly _editors: DiffEditorEditors, private readonly _diffModel: IObservable, private readonly _options: DiffEditorOptions, - private readonly _diffEditorWidget: DiffEditorWidget2, + private readonly _diffEditorWidget: DiffEditorWidget, private readonly _canIgnoreViewZoneUpdateEvent: () => boolean, @IClipboardService private readonly _clipboardService: IClipboardService, @IContextMenuService private readonly _contextMenuService: IContextMenuService, diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts b/src/vs/editor/browser/widget/diffEditor/movedBlocksLines.ts similarity index 98% rename from src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts rename to src/vs/editor/browser/widget/diffEditor/movedBlocksLines.ts index 54e19c99653..c67fd8822af 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/movedBlocksLines.ts +++ b/src/vs/editor/browser/widget/diffEditor/movedBlocksLines.ts @@ -13,9 +13,9 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, autorunHandleChanges, autorunWithStore, constObservable, derived, derivedWithStore, observableFromEvent, observableSignalFromEvent, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; import { ThemeIcon } from 'vs/base/common/themables'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; -import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; -import { PlaceholderViewZone, ViewZoneOverlayWidget, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/diffEditorEditors'; +import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; +import { PlaceholderViewZone, ViewZoneOverlayWidget, applyStyle, applyViewZones } from 'vs/editor/browser/widget/diffEditor/utils'; import { EditorLayoutInfo } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { OffsetRange, OffsetRangeSet } from 'vs/editor/common/core/offsetRange'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts b/src/vs/editor/browser/widget/diffEditor/outlineModel.ts similarity index 100% rename from src/vs/editor/browser/widget/diffEditorWidget2/outlineModel.ts rename to src/vs/editor/browser/widget/diffEditor/outlineModel.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts b/src/vs/editor/browser/widget/diffEditor/overviewRulerPart.ts similarity index 98% rename from src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts rename to src/vs/editor/browser/widget/diffEditor/overviewRulerPart.ts index bd93df81567..5b9ad1569b9 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget2/overviewRulerPart.ts +++ b/src/vs/editor/browser/widget/diffEditor/overviewRulerPart.ts @@ -11,9 +11,9 @@ import { Color } from 'vs/base/common/color'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservable, autorun, autorunWithStore, derived, observableFromEvent, observableSignalFromEvent } from 'vs/base/common/observable'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorEditors'; -import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; -import { appendRemoveOnDispose } from 'vs/editor/browser/widget/diffEditorWidget2/utils'; +import { DiffEditorEditors } from 'vs/editor/browser/widget/diffEditor/diffEditorEditors'; +import { DiffEditorViewModel } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; +import { appendRemoveOnDispose } from 'vs/editor/browser/widget/diffEditor/utils'; import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { Position } from 'vs/editor/common/core/position'; diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts b/src/vs/editor/browser/widget/diffEditor/renderLines.ts similarity index 100% rename from src/vs/editor/browser/widget/diffEditorWidget2/renderLines.ts rename to src/vs/editor/browser/widget/diffEditor/renderLines.ts diff --git a/src/vs/editor/browser/widget/media/diffEditor.css b/src/vs/editor/browser/widget/diffEditor/style.css similarity index 57% rename from src/vs/editor/browser/widget/media/diffEditor.css rename to src/vs/editor/browser/widget/diffEditor/style.css index 6d086fe1031..6e52932ef8e 100644 --- a/src/vs/editor/browser/widget/media/diffEditor.css +++ b/src/vs/editor/browser/widget/diffEditor/style.css @@ -2,6 +2,154 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .diff-hidden-lines-widget { + width: 100%; +} + +.monaco-editor .diff-hidden-lines { + height: 0px; /* The children each have a fixed height, the transform confuses the browser */ + transform: translate(0px, -10px); + font-size: 13px; + line-height: 14px; +} + +.monaco-editor .diff-hidden-lines:not(.dragging) .top:hover, +.monaco-editor .diff-hidden-lines:not(.dragging) .bottom:hover, +.monaco-editor .diff-hidden-lines .top.dragging, +.monaco-editor .diff-hidden-lines .bottom.dragging { + background-color: var(--vscode-focusBorder); +} + +.monaco-editor .diff-hidden-lines .top, +.monaco-editor .diff-hidden-lines .bottom { + transition: background-color 0.1s ease-out; + height: 4px; + background-color: transparent; + background-clip: padding-box; + border-bottom: 2px solid transparent; + border-top: 4px solid transparent; + cursor: ns-resize; +} + +.monaco-editor .diff-hidden-lines .top { + transform: translate(0px, 4px); +} + +.monaco-editor .diff-hidden-lines .bottom { + transform: translate(0px, -6px); +} + +.monaco-editor .diff-unchanged-lines { + background: var(--vscode-diffEditor-unchangedCodeBackground); +} + +.monaco-editor .noModificationsOverlay { + z-index: 1; + background: var(--vscode-editor-background); + + display: flex; + justify-content: center; + align-items: center; +} + + +.monaco-editor .diff-hidden-lines .center { + background: var(--vscode-diffEditor-unchangedRegionBackground); + color: var(--vscode-diffEditor-unchangedRegionForeground); + overflow: hidden; + display: block; + text-overflow: ellipsis; + white-space: nowrap; + + height: 24px; +} + +.monaco-editor .diff-hidden-lines .center span.codicon { + vertical-align: middle; +} + +.monaco-editor .diff-hidden-lines .center a:hover .codicon { + cursor: pointer; + color: var(--vscode-editorLink-activeForeground) !important; +} + +.monaco-editor .diff-hidden-lines div.breadcrumb-item { + cursor: pointer; +} + +.monaco-editor .diff-hidden-lines div.breadcrumb-item:hover { + color: var(--vscode-editorLink-activeForeground); +} + +.monaco-editor .movedOriginal { + border: 2px solid var(--vscode-diffEditor-move-border); +} + +.monaco-editor .movedModified { + border: 2px solid var(--vscode-diffEditor-move-border); +} + +.monaco-editor .movedOriginal.currentMove, .monaco-editor .movedModified.currentMove { + border: 2px solid var(--vscode-diffEditor-moveActive-border); +} + +.monaco-diff-editor .moved-blocks-lines path.currentMove { + stroke: var(--vscode-diffEditor-moveActive-border); +} + +.monaco-diff-editor .moved-blocks-lines path { + pointer-events: visiblestroke; +} + +.monaco-diff-editor .moved-blocks-lines .arrow { + fill: var(--vscode-diffEditor-move-border); +} + +.monaco-diff-editor .moved-blocks-lines .arrow.currentMove { + fill: var(--vscode-diffEditor-moveActive-border); +} + +.monaco-diff-editor .moved-blocks-lines .arrow-rectangle { + fill: var(--vscode-editor-background); +} + +.monaco-diff-editor .moved-blocks-lines { + position: absolute; + pointer-events: none; +} + +.monaco-diff-editor .moved-blocks-lines path { + fill: none; + stroke: var(--vscode-diffEditor-move-border); + stroke-width: 2; +} + +.monaco-editor .char-delete.diff-range-empty { + margin-left: -1px; + border-left: solid var(--vscode-diffEditor-removedTextBackground) 3px; +} + +.monaco-editor .char-insert.diff-range-empty { + border-left: solid var(--vscode-diffEditor-insertedTextBackground) 3px; +} + +.monaco-editor .fold-unchanged { + cursor: pointer; +} + +.monaco-diff-editor .diff-moved-code-block { + display: flex; + justify-content: flex-end; + margin-top: -4px; +} + +.monaco-diff-editor .diff-moved-code-block .action-bar .action-label.codicon { + width: 12px; + height: 12px; + font-size: 12px; +} + /* ---------- DiffEditor ---------- */ .monaco-diff-editor .diffOverview { diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/utils.ts b/src/vs/editor/browser/widget/diffEditor/utils.ts similarity index 100% rename from src/vs/editor/browser/widget/diffEditorWidget2/utils.ts rename to src/vs/editor/browser/widget/diffEditor/utils.ts diff --git a/src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts b/src/vs/editor/browser/widget/diffEditor/workerBasedDocumentDiffProvider.ts similarity index 100% rename from src/vs/editor/browser/widget/workerBasedDocumentDiffProvider.ts rename to src/vs/editor/browser/widget/diffEditor/workerBasedDocumentDiffProvider.ts diff --git a/src/vs/editor/browser/widget/diffEditorWidget2/style.css b/src/vs/editor/browser/widget/diffEditorWidget2/style.css deleted file mode 100644 index 93d6279c883..00000000000 --- a/src/vs/editor/browser/widget/diffEditorWidget2/style.css +++ /dev/null @@ -1,151 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-editor .diff-hidden-lines-widget { - width: 100%; -} - -.monaco-editor .diff-hidden-lines { - height: 0px; /* The children each have a fixed height, the transform confuses the browser */ - transform: translate(0px, -10px); - font-size: 13px; - line-height: 14px; -} - -.monaco-editor .diff-hidden-lines:not(.dragging) .top:hover, -.monaco-editor .diff-hidden-lines:not(.dragging) .bottom:hover, -.monaco-editor .diff-hidden-lines .top.dragging, -.monaco-editor .diff-hidden-lines .bottom.dragging { - background-color: var(--vscode-focusBorder); -} - -.monaco-editor .diff-hidden-lines .top, -.monaco-editor .diff-hidden-lines .bottom { - transition: background-color 0.1s ease-out; - height: 4px; - background-color: transparent; - background-clip: padding-box; - border-bottom: 2px solid transparent; - border-top: 4px solid transparent; - cursor: ns-resize; -} - -.monaco-editor .diff-hidden-lines .top { - transform: translate(0px, 4px); -} - -.monaco-editor .diff-hidden-lines .bottom { - transform: translate(0px, -6px); -} - -.monaco-editor .diff-unchanged-lines { - background: var(--vscode-diffEditor-unchangedCodeBackground); -} - -.monaco-editor .noModificationsOverlay { - z-index: 1; - background: var(--vscode-editor-background); - - display: flex; - justify-content: center; - align-items: center; -} - - -.monaco-editor .diff-hidden-lines .center { - background: var(--vscode-diffEditor-unchangedRegionBackground); - color: var(--vscode-diffEditor-unchangedRegionForeground); - overflow: hidden; - display: block; - text-overflow: ellipsis; - white-space: nowrap; - - height: 24px; -} - -.monaco-editor .diff-hidden-lines .center span.codicon { - vertical-align: middle; -} - -.monaco-editor .diff-hidden-lines .center a:hover .codicon { - cursor: pointer; - color: var(--vscode-editorLink-activeForeground) !important; -} - -.monaco-editor .diff-hidden-lines div.breadcrumb-item { - cursor: pointer; -} - -.monaco-editor .diff-hidden-lines div.breadcrumb-item:hover { - color: var(--vscode-editorLink-activeForeground); -} - -.monaco-editor .movedOriginal { - border: 2px solid var(--vscode-diffEditor-move-border); -} - -.monaco-editor .movedModified { - border: 2px solid var(--vscode-diffEditor-move-border); -} - -.monaco-editor .movedOriginal.currentMove, .monaco-editor .movedModified.currentMove { - border: 2px solid var(--vscode-diffEditor-moveActive-border); -} - -.monaco-diff-editor .moved-blocks-lines path.currentMove { - stroke: var(--vscode-diffEditor-moveActive-border); -} - -.monaco-diff-editor .moved-blocks-lines path { - pointer-events: visiblestroke; -} - -.monaco-diff-editor .moved-blocks-lines .arrow { - fill: var(--vscode-diffEditor-move-border); -} - -.monaco-diff-editor .moved-blocks-lines .arrow.currentMove { - fill: var(--vscode-diffEditor-moveActive-border); -} - -.monaco-diff-editor .moved-blocks-lines .arrow-rectangle { - fill: var(--vscode-editor-background); -} - -.monaco-diff-editor .moved-blocks-lines { - position: absolute; - pointer-events: none; -} - -.monaco-diff-editor .moved-blocks-lines path { - fill: none; - stroke: var(--vscode-diffEditor-move-border); - stroke-width: 2; -} - -.monaco-editor .char-delete.diff-range-empty { - margin-left: -1px; - border-left: solid var(--vscode-diffEditor-removedTextBackground) 3px; -} - -.monaco-editor .char-insert.diff-range-empty { - border-left: solid var(--vscode-diffEditor-insertedTextBackground) 3px; -} - -.monaco-editor .fold-unchanged { - cursor: pointer; -} - -.monaco-diff-editor .diff-moved-code-block { - display: flex; - justify-content: flex-end; - margin-top: -4px; -} - -.monaco-diff-editor .diff-moved-code-block .action-bar .action-label.codicon { - width: 12px; - height: 12px; - font-size: 12px; -} diff --git a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts index 78d8dffd609..8d63ee6f754 100644 --- a/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/embeddedCodeEditorWidget.ts @@ -7,7 +7,7 @@ import * as objects from 'vs/base/common/objects'; import { ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget2, IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget, IDiffCodeEditorWidgetOptions } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { ConfigurationChangedEvent, IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; @@ -66,10 +66,7 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { } } -/** - * TODO: Rename to EmbeddedDiffEditorWidget once EmbeddedDiffEditorWidget is removed. - */ -export class EmbeddedDiffEditorWidget2 extends DiffEditorWidget2 { +export class EmbeddedDiffEditorWidget extends DiffEditorWidget { private readonly _parentEditor: ICodeEditor; private readonly _overwriteOptions: IDiffEditorOptions; diff --git a/src/vs/editor/browser/widget/inlineDiffMargin.ts b/src/vs/editor/browser/widget/inlineDiffMargin.ts deleted file mode 100644 index 1388f64a8f0..00000000000 --- a/src/vs/editor/browser/widget/inlineDiffMargin.ts +++ /dev/null @@ -1,231 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as nls from 'vs/nls'; -import * as dom from 'vs/base/browser/dom'; -import { Action } from 'vs/base/common/actions'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { Range } from 'vs/editor/common/core/range'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { Codicon } from 'vs/base/common/codicons'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model'; -import { isIOS } from 'vs/base/common/platform'; - -export interface IDiffLinesChange { - readonly originalStartLineNumber: number; - readonly originalEndLineNumber: number; - readonly modifiedStartLineNumber: number; - readonly modifiedEndLineNumber: number; - readonly originalModel: ITextModel; - viewLineCounts: number[] | null; -} - -export class InlineDiffMargin extends Disposable { - private readonly _diffActions: HTMLElement; - - private _visibility: boolean = false; - - get visibility(): boolean { - return this._visibility; - } - - set visibility(_visibility: boolean) { - if (this._visibility !== _visibility) { - this._visibility = _visibility; - - if (_visibility) { - this._diffActions.style.visibility = 'visible'; - } else { - this._diffActions.style.visibility = 'hidden'; - } - } - } - - constructor( - private readonly _viewZoneId: string, - private readonly _marginDomNode: HTMLElement, - public readonly editor: CodeEditorWidget, - public readonly diff: IDiffLinesChange, - private readonly _contextMenuService: IContextMenuService, - private readonly _clipboardService: IClipboardService - ) { - super(); - - // make sure the diff margin shows above overlay. - this._marginDomNode.style.zIndex = '10'; - - this._diffActions = document.createElement('div'); - this._diffActions.className = ThemeIcon.asClassName(Codicon.lightBulb) + ' lightbulb-glyph'; - this._diffActions.style.position = 'absolute'; - const lineHeight = editor.getOption(EditorOption.lineHeight); - const lineFeed = editor.getModel()!.getEOL(); - this._diffActions.style.right = '0px'; - this._diffActions.style.visibility = 'hidden'; - this._diffActions.style.height = `${lineHeight}px`; - this._diffActions.style.lineHeight = `${lineHeight}px`; - this._marginDomNode.appendChild(this._diffActions); - - const actions: Action[] = []; - const isDeletion = diff.modifiedEndLineNumber === 0; - - // default action - actions.push(new Action( - 'diff.clipboard.copyDeletedContent', - isDeletion - ? (diff.originalEndLineNumber > diff.modifiedStartLineNumber - ? nls.localize('diff.clipboard.copyDeletedLinesContent.label', "Copy deleted lines") - : nls.localize('diff.clipboard.copyDeletedLinesContent.single.label', "Copy deleted line")) - : (diff.originalEndLineNumber > diff.modifiedStartLineNumber - ? nls.localize('diff.clipboard.copyChangedLinesContent.label', "Copy changed lines") - : nls.localize('diff.clipboard.copyChangedLinesContent.single.label', "Copy changed line")), - undefined, - true, - async () => { - const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber + 1, 1); - const deletedText = diff.originalModel.getValueInRange(range); - await this._clipboardService.writeText(deletedText); - } - )); - - let currentLineNumberOffset = 0; - let copyLineAction: Action | undefined = undefined; - if (diff.originalEndLineNumber > diff.modifiedStartLineNumber) { - copyLineAction = new Action( - 'diff.clipboard.copyDeletedLineContent', - isDeletion - ? nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", diff.originalStartLineNumber) - : nls.localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", diff.originalStartLineNumber), - undefined, - true, - async () => { - const lineContent = diff.originalModel.getLineContent(diff.originalStartLineNumber + currentLineNumberOffset); - if (lineContent === '') { - // empty line - const eof = diff.originalModel.getEndOfLineSequence(); - await this._clipboardService.writeText(eof === EndOfLineSequence.LF ? '\n' : '\r\n'); - } else { - await this._clipboardService.writeText(lineContent); - } - } - ); - - actions.push(copyLineAction); - } - - const readOnly = editor.getOption(EditorOption.readOnly); - if (!readOnly) { - actions.push(new Action('diff.inline.revertChange', nls.localize('diff.inline.revertChange.label', "Revert this change"), undefined, true, async () => { - const range = new Range(diff.originalStartLineNumber, 1, diff.originalEndLineNumber, diff.originalModel.getLineMaxColumn(diff.originalEndLineNumber)); - const deletedText = diff.originalModel.getValueInRange(range); - if (diff.modifiedEndLineNumber === 0) { - // deletion only - const column = editor.getModel()!.getLineMaxColumn(diff.modifiedStartLineNumber); - editor.executeEdits('diffEditor', [ - { - range: new Range(diff.modifiedStartLineNumber, column, diff.modifiedStartLineNumber, column), - text: lineFeed + deletedText - } - ]); - } else { - const column = editor.getModel()!.getLineMaxColumn(diff.modifiedEndLineNumber); - editor.executeEdits('diffEditor', [ - { - range: new Range(diff.modifiedStartLineNumber, 1, diff.modifiedEndLineNumber, column), - text: deletedText - } - ]); - } - - })); - } - - const useShadowDOM = editor.getOption(EditorOption.useShadowDOM) && !isIOS; // Do not use shadow dom on IOS #122035 - - const showContextMenu = (x: number, y: number) => { - this._contextMenuService.showContextMenu({ - domForShadowRoot: useShadowDOM ? editor.getDomNode() ?? undefined : undefined, - getAnchor: () => { - return { - x, - y - }; - }, - getActions: () => { - if (copyLineAction) { - copyLineAction.label = - isDeletion - ? nls.localize('diff.clipboard.copyDeletedLineContent.label', "Copy deleted line ({0})", diff.originalStartLineNumber + currentLineNumberOffset) - : nls.localize('diff.clipboard.copyChangedLineContent.label', "Copy changed line ({0})", diff.originalStartLineNumber + currentLineNumberOffset); - } - return actions; - }, - autoSelectFirstItem: true - }); - }; - - this._register(dom.addStandardDisposableListener(this._diffActions, 'mousedown', e => { - const { top, height } = dom.getDomNodePagePosition(this._diffActions); - const pad = Math.floor(lineHeight / 3); - e.preventDefault(); - - showContextMenu(e.posx, top + height + pad); - - })); - - this._register(editor.onMouseMove((e: IEditorMouseEvent) => { - if (e.target.type === MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === MouseTargetType.GUTTER_VIEW_ZONE) { - const viewZoneId = e.target.detail.viewZoneId; - - if (viewZoneId === this._viewZoneId) { - this.visibility = true; - currentLineNumberOffset = this._updateLightBulbPosition(this._marginDomNode, e.event.browserEvent.y, lineHeight); - } else { - this.visibility = false; - } - } else { - this.visibility = false; - } - })); - - this._register(editor.onMouseDown((e: IEditorMouseEvent) => { - if (!e.event.rightButton) { - return; - } - - if (e.target.type === MouseTargetType.CONTENT_VIEW_ZONE || e.target.type === MouseTargetType.GUTTER_VIEW_ZONE) { - const viewZoneId = e.target.detail.viewZoneId; - - if (viewZoneId === this._viewZoneId) { - e.event.preventDefault(); - currentLineNumberOffset = this._updateLightBulbPosition(this._marginDomNode, e.event.browserEvent.y, lineHeight); - showContextMenu(e.event.posx, e.event.posy + lineHeight); - } - } - })); - } - - private _updateLightBulbPosition(marginDomNode: HTMLElement, y: number, lineHeight: number): number { - const { top } = dom.getDomNodePagePosition(marginDomNode); - const offset = y - top; - const lineNumberOffset = Math.floor(offset / lineHeight); - const newTop = lineNumberOffset * lineHeight; - this._diffActions.style.top = `${newTop}px`; - if (this.diff.viewLineCounts) { - let acc = 0; - for (let i = 0; i < this.diff.viewLineCounts.length; i++) { - acc += this.diff.viewLineCounts[i]; - if (lineNumberOffset < acc) { - return i; - } - } - } - return lineNumberOffset; - } -} diff --git a/src/vs/editor/editor.all.ts b/src/vs/editor/editor.all.ts index 8cb672bb8dc..cb83b05a9de 100644 --- a/src/vs/editor/editor.all.ts +++ b/src/vs/editor/editor.all.ts @@ -5,7 +5,7 @@ import 'vs/editor/browser/coreCommands'; import 'vs/editor/browser/widget/codeEditorWidget'; -import 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import 'vs/editor/browser/widget/diffEditor/diffEditor.contribution'; import 'vs/editor/contrib/anchorSelect/browser/anchorSelect'; import 'vs/editor/contrib/bracketMatching/browser/bracketMatching'; import 'vs/editor/contrib/caretOperations/browser/caretOperations'; diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 7138127f621..02ff64c602b 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -36,7 +36,7 @@ import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry' import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { IAudioCueService } from 'vs/platform/audioCues/browser/audioCueService'; /** @@ -480,7 +480,7 @@ export class StandaloneEditor extends StandaloneCodeEditor implements IStandalon } } -export class StandaloneDiffEditor2 extends DiffEditorWidget2 implements IStandaloneDiffEditor { +export class StandaloneDiffEditor2 extends DiffEditorWidget implements IStandaloneDiffEditor { private readonly _configurationService: IConfigurationService; private readonly _standaloneThemeService: IStandaloneThemeService; diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts index 63b7f923b00..ad354a95740 100644 --- a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts +++ b/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { UnchangedRegion } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorViewModel'; +import { UnchangedRegion } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; import { LineRange } from 'vs/editor/common/core/lineRange'; import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 2fb64d37538..36f68774d5f 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -68,7 +68,6 @@ import { UntitledTextEditorInputSerializer, UntitledTextEditorWorkingCopyEditorH import { DynamicEditorConfigurations } from 'vs/workbench/browser/parts/editor/editorConfiguration'; import { AccessibilityStatus } from 'vs/workbench/browser/parts/editor/accessibilityStatus'; import { ToggleTabsVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; -import 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2.contribution'; //#region Editor Registrations diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 58377fcf767..5cbf571c2d2 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -34,7 +34,7 @@ import { ByteSize, FileOperationError, FileOperationResult, IFileService, TooLar import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { StopWatch } from 'vs/base/common/stopwatch'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; /** * The text editor that leverages the diff text editor for the editing experience. @@ -80,7 +80,7 @@ export class TextDiffEditor extends AbstractTextEditor imp } protected override createEditorControl(parent: HTMLElement, configuration: ICodeEditorOptions): void { - this.diffEditorControl = this._register(this.instantiationService.createInstance(DiffEditorWidget2, parent, configuration, {})); + this.diffEditorControl = this._register(this.instantiationService.createInstance(DiffEditorWidget, parent, configuration, {})); } protected updateEditorControlOptions(options: ICodeEditorOptions): void { @@ -313,7 +313,7 @@ export class TextDiffEditor extends AbstractTextEditor imp private logInputLifecycleTelemetry(duration: number, languageId: string | undefined): void { let collapseUnchangedRegions = false; - if (this.diffEditorControl instanceof DiffEditorWidget2) { + if (this.diffEditorControl instanceof DiffEditorWidget) { collapseUnchangedRegions = this.diffEditorControl.collapseUnchangedRegions; } this.telemetryService.publicLog2<{ diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index e277b372021..f8048ff34b9 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -12,7 +12,7 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; -import { AccessibleDiffViewerNext } from 'vs/editor/browser/widget/diffEditor.contribution'; +import { AccessibleDiffViewerNext } from 'vs/editor/browser/widget/diffEditor/diffEditor.contribution'; export function getAccessibilityHelpText(accessor: ServicesAccessor, type: 'panelChat' | 'inlineChat'): string { const keybindingService = accessor.get(IKeybindingService); diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index d8c0d322bf3..0e9a204c87f 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -8,23 +8,23 @@ import { autorunWithStore, observableFromEvent } from 'vs/base/common/observable import { IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { registerDiffEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev } from 'vs/editor/browser/widget/diffEditor.contribution'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; -import { EmbeddedDiffEditorWidget2 } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { AccessibleDiffViewerNext, AccessibleDiffViewerPrev } from 'vs/editor/browser/widget/diffEditor/diffEditor.contribution'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { IDiffEditorContribution } from 'vs/editor/common/editorCommon'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { Registry } from 'vs/platform/registry/common/platform'; import { FloatingEditorClickWidget } from 'vs/workbench/browser/codeeditor'; +import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { AccessibleViewType, IAccessibleViewService } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; class DiffEditorHelperContribution extends Disposable implements IDiffEditorContribution { public static readonly ID = 'editor.contrib.diffEditorHelper'; @@ -39,7 +39,7 @@ class DiffEditorHelperContribution extends Disposable implements IDiffEditorCont this._register(createScreenReaderHelp()); - const isEmbeddedDiffEditor = this._diffEditor instanceof EmbeddedDiffEditorWidget2; + const isEmbeddedDiffEditor = this._diffEditor instanceof EmbeddedDiffEditorWidget; if (!isEmbeddedDiffEditor) { const computationResult = observableFromEvent(e => this._diffEditor.onDidUpdateDiff(e), () => this._diffEditor.getDiffComputationResult()); @@ -92,7 +92,7 @@ function createScreenReaderHelp(): IDisposable { const next = keybindingService.lookupKeybinding(AccessibleDiffViewerNext.id)?.getAriaLabel(); const previous = keybindingService.lookupKeybinding(AccessibleDiffViewerPrev.id)?.getAriaLabel(); - if (!(editorService.activeTextEditorControl instanceof DiffEditorWidget2)) { + if (!(editorService.activeTextEditorControl instanceof DiffEditorWidget)) { return; } @@ -115,10 +115,7 @@ function createScreenReaderHelp(): IDisposable { }, options: { type: AccessibleViewType.Help } }); - }, ContextKeyExpr.and( - ContextKeyEqualsExpr.create('diffEditorVersion', 2), - ContextKeyEqualsExpr.create('isInDiffEditor', true) - )); + }, ContextKeyEqualsExpr.create('isInDiffEditor', true)); } registerDiffEditorContribution(DiffEditorHelperContribution.ID, DiffEditorHelperContribution); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 05bb019ec59..c90d70f10e9 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -7,7 +7,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget2 } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, InlineChatResponseType, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChateResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, MENU_INLINE_CHAT_WIDGET_TOGGLE, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_INNER_CURSOR_END } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -123,7 +123,7 @@ abstract class AbstractInlineChatAction extends EditorAction2 { if (!ctrl) { for (const diffEditor of accessor.get(ICodeEditorService).listDiffEditors()) { if (diffEditor.getOriginalEditor() === editor || diffEditor.getModifiedEditor() === editor) { - if (diffEditor instanceof EmbeddedDiffEditorWidget2) { + if (diffEditor instanceof EmbeddedDiffEditorWidget) { this.runEditorCommand(accessor, diffEditor.getParentEditor(), ..._args); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index a9b339e7ae5..0f153ca64da 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -7,7 +7,7 @@ import { Dimension, h } from 'vs/base/browser/dom'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { assertType } from 'vs/base/common/types'; import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget2 } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; @@ -64,7 +64,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { .getEditorContributions() .filter(c => c.id !== INLINE_CHAT_ID && c.id !== FoldingController.ID); - this._diffEditor = instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.domNode, { + this._diffEditor = instantiationService.createInstance(EmbeddedDiffEditorWidget, this._elements.domNode, { scrollbar: { useShadows: false, alwaysConsumeMouseWheel: false }, scrollBeyondLastLine: false, renderMarginRevertIcon: true, diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 9703af6a00a..370a58cbc05 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -22,7 +22,7 @@ import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; import { SnippetController2 } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { IModelService } from 'vs/editor/common/services/model'; import { URI } from 'vs/base/common/uri'; -import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget2 } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; +import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { HiddenItemStrategy, MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; @@ -170,7 +170,7 @@ export class InlineChatWidget { private readonly _progressBar: ProgressBar; - private readonly _previewDiffEditor: IdleValue; + private readonly _previewDiffEditor: IdleValue; private readonly _previewDiffModel = this._store.add(new MutableDisposable()); private readonly _previewCreateTitle: ResourceLabel; @@ -359,7 +359,7 @@ export class InlineChatWidget { this._store.add(feedbackToolbar); // preview editors - this._previewDiffEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget2, this._elements.previewDiff, { + this._previewDiffEditor = this._store.add(new IdleValue(() => this._store.add(_instantiationService.createInstance(EmbeddedDiffEditorWidget, this._elements.previewDiff, { ..._previewEditorEditorOptions, onlyShowAccessibleDiffViewer: this._accessibilityService.isScreenReaderOptimized(), }, { modifiedEditor: codeEditorWidgetOptions, originalEditor: codeEditorWidgetOptions }, parentEditor)))); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 369eef0d3cf..f405b53b5e0 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -41,7 +41,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { fixedDiffEditorOptions, fixedEditorOptions, fixedEditorPadding } from 'vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { return { @@ -242,7 +242,7 @@ abstract class AbstractElementRenderer extends Disposable { protected _metadataInfoContainer!: HTMLElement; protected _metadataEditorContainer?: HTMLElement; protected _metadataEditorDisposeStore!: DisposableStore; - protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget2; + protected _metadataEditor?: CodeEditorWidget | DiffEditorWidget; protected _outputHeaderContainer!: HTMLElement; protected _outputHeader!: PropertyHeader; @@ -256,8 +256,8 @@ abstract class AbstractElementRenderer extends Disposable { protected _outputLeftView?: OutputContainer; protected _outputRightView?: OutputContainer; protected _outputEditorDisposeStore!: DisposableStore; - protected _outputEditor?: CodeEditorWidget | DiffEditorWidget2; - protected _outputMetadataEditor?: DiffEditorWidget2; + protected _outputEditor?: CodeEditorWidget | DiffEditorWidget; + protected _outputMetadataEditor?: DiffEditorWidget; protected _diffEditorContainer!: HTMLElement; protected _diagonalFill?: HTMLElement; @@ -493,7 +493,7 @@ abstract class AbstractElementRenderer extends Disposable { this._metadataEditorDisposeStore.clear(); if (this.cell instanceof SideBySideDiffElementViewModel) { - this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget2, this._metadataEditorContainer!, { + this._metadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._metadataEditorContainer!, { ...fixedDiffEditorOptions, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: false, @@ -606,7 +606,7 @@ abstract class AbstractElementRenderer extends Disposable { const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; const lineCount = Math.max(originalModel.getLineCount(), modifiedModel.getLineCount()); - this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget2, this._outputEditorContainer!, { + this._outputEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputEditorContainer!, { ...fixedDiffEditorOptions, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: true, @@ -1214,7 +1214,7 @@ export class InsertElement extends SingleSideDiffElement { } export class ModifiedElement extends AbstractElementRenderer { - private _editor?: DiffEditorWidget2; + private _editor?: DiffEditorWidget; private _editorViewStateChanged: boolean; private _editorContainer!: HTMLElement; private _inputToolbarContainer!: HTMLElement; @@ -1416,7 +1416,7 @@ export class ModifiedElement extends AbstractElementRenderer { this._outputMetadataContainer.style.top = `${this.cell.layoutInfo.rawOutputHeight}px`; // single output, metadata change, let's render a diff editor for metadata - this._outputMetadataEditor = this.instantiationService.createInstance(DiffEditorWidget2, this._outputMetadataContainer!, { + this._outputMetadataEditor = this.instantiationService.createInstance(DiffEditorWidget, this._outputMetadataContainer!, { ...fixedDiffEditorOptions, overflowWidgetsDomNode: this.notebookEditor.getOverflowContainerDomNode(), readOnly: true, diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts index bd56f8c7ff4..7d6f1068778 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffElementViewModel.ts @@ -8,7 +8,7 @@ import { hash } from 'vs/base/common/hash'; import { toFormattedString } from 'vs/base/common/jsonFormatter'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { fixedEditorPadding } from 'vs/workbench/contrib/notebook/browser/diff/diffCellEditorOptions'; @@ -341,10 +341,10 @@ export abstract class DiffElementViewModelBase extends Disposable { getComputedCellContainerWidth(layoutInfo: NotebookLayoutInfo, diffEditor: boolean, fullWidth: boolean) { if (fullWidth) { - return layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget2.ENTIRE_DIFF_OVERVIEW_WIDTH : 0) - 2; + return layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0) - 2; } - return (layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget2.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)) / 2 - 18 - 2; + return (layoutInfo.width - 2 * DIFF_CELL_MARGIN + (diffEditor ? DiffEditorWidget.ENTIRE_DIFF_OVERVIEW_WIDTH : 0)) / 2 - 18 - 2; } getOutputEditorViewState(): editorCommon.ICodeEditorViewState | editorCommon.IDiffEditorViewState | null { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 62601268ebd..6741f2a3ab8 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -15,7 +15,7 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; export enum DiffSide { Original = 0, @@ -85,7 +85,7 @@ export interface CellDiffSideBySideRenderTemplate extends CellDiffCommonRenderTe readonly body: HTMLElement; readonly diffEditorContainer: HTMLElement; readonly elementDisposables: DisposableStore; - readonly sourceEditor: DiffEditorWidget2; + readonly sourceEditor: DiffEditorWidget; readonly editorContainer: HTMLElement; readonly inputToolbarContainer: HTMLElement; readonly toolbar: WorkbenchToolBar; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index af0bb4a20f4..ad6676cab7e 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -18,7 +18,7 @@ import { DiffElementViewModelBase, SideBySideDiffElementViewModel, SingleSideDif import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DIFF_CELL_MARGIN, INotebookTextDiffEditor } from 'vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser'; import { DeletedElement, getOptimizedNestedCodeEditorWidgetOptions, InsertElement, ModifiedElement } from 'vs/workbench/contrib/notebook/browser/diff/diffComponents'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; -import { DiffEditorWidget2 } from 'vs/editor/browser/widget/diffEditorWidget2/diffEditorWidget2'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -234,7 +234,7 @@ export class CellDiffSideBySideRenderer implements IListRenderer()); + private readonly widget = this._register(new MutableDisposable()); private readonly model = this._register(new MutableDisposable()); private dimension?: dom.IDimension; @@ -1178,13 +1178,13 @@ class DiffContentProvider extends Disposable implements IPeekOutputRenderer { const model = this.model.value = new SimpleDiffEditorModel(original, modified); if (!this.widget.value) { this.widget.value = this.editor ? this.instantiationService.createInstance( - EmbeddedDiffEditorWidget2, + EmbeddedDiffEditorWidget, this.container, diffEditorOptions, {}, this.editor, ) : this.instantiationService.createInstance( - DiffEditorWidget2, + DiffEditorWidget, this.container, diffEditorOptions, {}, @@ -1604,7 +1604,7 @@ function getOuterEditorFromDiffEditor(codeEditorService: ICodeEditorService): IC const diffEditors = codeEditorService.listDiffEditors(); for (const diffEditor of diffEditors) { - if (diffEditor.hasTextFocus() && diffEditor instanceof EmbeddedDiffEditorWidget2) { + if (diffEditor.hasTextFocus() && diffEditor instanceof EmbeddedDiffEditorWidget) { return diffEditor.getParentEditor(); } } From 1ad9d25cd573d461b9ae11b003485a8b9bce689d Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 6 Sep 2023 15:16:51 +0200 Subject: [PATCH 564/607] Improves leak detection reporting. --- src/vs/base/test/common/utils.ts | 94 ++++++++++++++++++++++++++------ 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/src/vs/base/test/common/utils.ts b/src/vs/base/test/common/utils.ts index 188ca4f19bd..3301cdfe614 100644 --- a/src/vs/base/test/common/utils.ts +++ b/src/vs/base/test/common/utils.ts @@ -3,9 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { compareBy, numberComparator } from 'vs/base/common/arrays'; +import { SetMap, groupBy } from 'vs/base/common/collections'; import { DisposableStore, IDisposable, IDisposableTracker, setDisposableTracker } from 'vs/base/common/lifecycle'; import { join } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; +import { trim } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; export type ValueCallback = (value: T | Promise) => void; @@ -41,19 +44,20 @@ export async function assertThrowsAsync(block: () => any, message: string | Erro throw err; } -interface DisposableData { +interface DisposableInfo { + value: IDisposable; source: string | null; parent: IDisposable | null; isSingleton: boolean; } export class DisposableTracker implements IDisposableTracker { - private readonly livingDisposables = new Map(); + private readonly livingDisposables = new Map(); private getDisposableData(d: IDisposable) { let val = this.livingDisposables.get(d); if (!val) { - val = { parent: null, source: null, isSingleton: false }; + val = { parent: null, source: null, isSingleton: false, value: d }; this.livingDisposables.set(d, val); } return val; @@ -80,7 +84,7 @@ export class DisposableTracker implements IDisposableTracker { this.getDisposableData(disposable).isSingleton = true; } - private getRootParent(data: DisposableData, cache: Map): DisposableData { + private getRootParent(data: DisposableInfo, cache: Map): DisposableInfo { const cacheValue = cache.get(data); if (cacheValue) { return cacheValue; @@ -92,7 +96,7 @@ export class DisposableTracker implements IDisposableTracker { } getTrackedDisposables() { - const rootParentCache = new Map(); + const rootParentCache = new Map(); const leaking = [...this.livingDisposables.entries()] .filter(([, v]) => v.source !== null && !this.getRootParent(v, rootParentCache).isSingleton) @@ -103,25 +107,79 @@ export class DisposableTracker implements IDisposableTracker { } ensureNoLeakingDisposables() { - const rootParentCache = new Map(); - const leaking = [...this.livingDisposables.values()] - .filter(v => v.source !== null && !this.getRootParent(v, rootParentCache).isSingleton); + const rootParentCache = new Map(); - if (leaking.length > 0) { - const count = 10; - const firstLeaking = leaking.slice(0, count); - const remainingCount = leaking.length - count; + const leakingObjects = [...this.livingDisposables.values()] + .filter((info) => info.source !== null && !this.getRootParent(info, rootParentCache).isSingleton); - const separator = '\n--------------------\n\n'; - let s = firstLeaking.map(l => l.source).join(separator); - if (remainingCount > 0) { - s += `${separator}+ ${remainingCount} more`; + if (leakingObjects.length === 0) { + return; + } + const leakingObjsSet = new Set(leakingObjects.map(o => o.value)); + + // Remove all objects that are a child of other leaking objects. Assumes there are no cycles. + const uncoveredLeakingObjs = leakingObjects.filter(l => { + return !(l.parent && leakingObjsSet.has(l.parent)); + }); + + if (uncoveredLeakingObjs.length === 0) { + throw new Error('There are cyclic diposable chains!'); + } + + function getStackTracePath(leaking: DisposableInfo): string[] { + function removePrefix(array: string[], linesToRemove: (string | RegExp)[]) { + while (array.length > 0 && linesToRemove.some(regexp => typeof regexp === 'string' ? regexp === array[0] : array[0].match(regexp))) { + array.shift(); + } } - throw new Error(`These disposables were not disposed:\n${s}`); + const lines = leaking.source!.split('\n').map(p => trim(p.trim(), 'at ')).filter(l => l !== ''); + removePrefix(lines, ['Error', /^trackDisposable \(.*\)$/, /^DisposableTracker.trackDisposable \(.*\)$/]); + return lines.reverse(); } - } + const stackTraceStarts = new SetMap(); + for (const leaking of uncoveredLeakingObjs) { + const stackTracePath = getStackTracePath(leaking); + for (let i = 0; i <= stackTracePath.length; i++) { + stackTraceStarts.add(stackTracePath.slice(0, i).join('\n'), leaking); + } + } + + uncoveredLeakingObjs.sort(compareBy(l => getStackTracePath(l).length, numberComparator)); + + const maxReported = 10; + + let i = 0; + for (const leaking of uncoveredLeakingObjs.slice(0, maxReported)) { + i++; + const stackTracePath = getStackTracePath(leaking); + const stackTraceFormattedLines = []; + + for (let i = 0; i < stackTracePath.length; i++) { + let line = stackTracePath[i]; + const starts = stackTraceStarts.get(stackTracePath.slice(0, i + 1).join('\n')); + line = `(shared with ${starts.size}/${uncoveredLeakingObjs.length} leaks) at ${line}`; + + const prevStarts = stackTraceStarts.get(stackTracePath.slice(0, i).join('\n')); + const continuations = groupBy([...prevStarts].map(d => getStackTracePath(d)[i]), v => v); + delete continuations[stackTracePath[i]]; + for (const [cont, set] of Object.entries(continuations)) { + stackTraceFormattedLines.unshift(` - stacktraces of ${set.length} other leaks continue with ${cont}`); + } + + stackTraceFormattedLines.unshift(line); + } + + console.error(`\n\n\n==================== Leaking disposable ${i}/${uncoveredLeakingObjs.length}: ${leaking.value.constructor.name} ====================\n${stackTraceFormattedLines.join('\n')}\n============================================================\n\n`); + } + + if (uncoveredLeakingObjs.length > maxReported) { + console.error(`\n\n\n... and ${uncoveredLeakingObjs.length - maxReported} more leaking disposables\n\n`); + } + + throw new Error(`There are ${uncoveredLeakingObjs.length} undisposed disposables! (check test output)`); + } } /** From b254a4c05a4b7625970e7fee3cbcb3dbf2862bba Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Wed, 6 Sep 2023 16:27:56 +0200 Subject: [PATCH 565/607] Fixes CI --- src/vs/base/test/common/lifecycle.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index b3d97a77a9c..343167bd57d 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -232,7 +232,7 @@ suite('No Leakage Utilities', () => { // noop }); }); - }, e => e.message.indexOf('These disposables were not disposed') !== -1); + }, e => e.message.indexOf('undisposed disposables') !== -1); }); test('throws if a disposable is not disposed', () => { @@ -240,7 +240,7 @@ suite('No Leakage Utilities', () => { throwIfDisposablesAreLeaked(() => { new DisposableStore(); }); - }, e => e.message.indexOf('These disposables were not disposed') !== -1); + }, e => e.message.indexOf('undisposed disposables') !== -1); }); test('does not throw if all event subscriptions are cleaned up', () => { From f23b27c5cd1567b25ba7e030a06a1367331e337a Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 6 Sep 2023 10:37:26 -0700 Subject: [PATCH 566/607] feat: support format on type in SCM input (#192310) I exclusively use the SCM input box for writing commit messages and have wanted the ability to wrap my commit messages to fit the 50/72 rule so they show up nicely on GitHub. @connor4312 wraps commit messages but by using a vertical editor ruler. Since the SCM input box is hooked up to code actions, I've implemented a quick fix for the input box that shows up with `Ctrl+.` (https://github.com/joyceerhl/vscode-git-fit-commit), but since the lightbulb is disabled for the SCM input, it's easy to miss that a fix is available. I would also like to make formatting more automatic, but unfortunately the SCM input isn't hooked up to format on type yet--so this PR enables format on type for the SCM input box. --- src/vs/editor/contrib/format/browser/formatActions.ts | 2 +- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/format/browser/formatActions.ts b/src/vs/editor/contrib/format/browser/formatActions.ts index 2abca269100..6814b36c485 100644 --- a/src/vs/editor/contrib/format/browser/formatActions.ts +++ b/src/vs/editor/contrib/format/browser/formatActions.ts @@ -27,7 +27,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IEditorProgressService, Progress } from 'vs/platform/progress/common/progress'; -class FormatOnType implements IEditorContribution { +export class FormatOnType implements IEditorContribution { public static readonly ID = 'editor.contrib.autoFormat'; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index a5e80711931..9060e9dbdca 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -96,6 +96,7 @@ import { IDragAndDropData } from 'vs/base/browser/dnd'; import { fillEditorsDragData } from 'vs/workbench/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; +import { FormatOnType } from 'vs/editor/contrib/format/browser/formatActions'; type TreeElement = ISCMRepository | ISCMInput | ISCMActionButton | ISCMResourceGroup | IResourceNode | ISCMResource; @@ -2025,6 +2026,7 @@ class SCMInputWidget { quickSuggestions: false, scrollbar: { alwaysConsumeMouseWheel: false }, overflowWidgetsDomNode, + formatOnType: true, renderWhitespace: 'none', dropIntoEditor: { enabled: true } }; @@ -2045,6 +2047,7 @@ class SCMInputWidget { SuggestController.ID, InlineCompletionsController.ID, CodeActionController.ID, + FormatOnType.ID ]) }; From a50759cfeb90350a608a995d02031bcc01c05c08 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 6 Sep 2023 11:29:16 -0700 Subject: [PATCH 567/607] Add setting to disable chat history variables (#192319) --- .../contrib/chat/browser/contrib/chatInputEditorContrib.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index d4ff01aec83..e110d6ad3cc 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -15,6 +15,7 @@ import { CompletionContext, CompletionItem, CompletionItemKind, CompletionList } import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Registry } from 'vs/platform/registry/common/platform'; import { inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -357,6 +358,7 @@ class VariableCompletions extends Disposable { @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, @IChatVariablesService private readonly chatVariablesService: IChatVariablesService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); @@ -389,13 +391,14 @@ class VariableCompletions extends Disposable { .filter(isResponseVM); // TODO@roblourens work out a real API for this- maybe it can be part of the two-step flow that @file will probably use - const historyItems = history.map((h, i): CompletionItem => ({ + const historyVariablesEnabled = this.configurationService.getValue('chat.experimental.historyVariables'); + const historyItems = historyVariablesEnabled ? history.map((h, i): CompletionItem => ({ label: `@response:${i + 1}`, detail: h.response.asString(), insertText: `@response:${String(i + 1).padStart(String(history.length).length, '0')} `, kind: CompletionItemKind.Text, range: { insert, replace }, - })); + })) : []; const variableItems = Array.from(this.chatVariablesService.getVariables()).map(v => { const withAt = `@${v.name}`; From 4747d36068d364c3529de0f269665431a5718d62 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 6 Sep 2023 11:31:29 -0700 Subject: [PATCH 568/607] tasks: use a type union for ITaskEvent (#192314) When fixing a bug in tests I noticed that all properties of the ITaskEvent were optional, so I had to go into task code to figure out what types I could use. This PR refactors the types so ITaskEvent is a union type that provides as much information as possible about the presence of properties. I tested this on some projects but am not sure if there's a repo/project to test out the full gamut of task functionality. But there should be no (barring once, see comment) functional change. --- .../workbench/api/browser/mainThreadTask.ts | 6 +- .../tasks/browser/abstractTaskService.ts | 4 +- .../tasks/browser/task.contribution.ts | 6 +- .../tasks/browser/taskTerminalStatus.ts | 12 +- .../tasks/browser/terminalTaskSystem.ts | 59 ++++---- .../workbench/contrib/tasks/common/tasks.ts | 142 +++++++++++++----- .../test/browser/taskTerminalStatus.test.ts | 6 +- 7 files changed, 150 insertions(+), 85 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 8db946cea29..b24abe59657 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -429,7 +429,11 @@ export class MainThreadTask implements MainThreadTaskShape { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTask); this._providers = new Map(); this._taskService.onDidStateChange(async (event: ITaskEvent) => { - const task = event.__task!; + if (event.kind === TaskEventKind.Changed) { + return; + } + + const task = event.__task; if (event.kind === TaskEventKind.Start) { const execution = TaskExecutionDTO.from(task.getTaskExecution()); let resolvedDefinition: ITaskDefinitionDTO = execution.task!.definition; diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 146b717e95c..820c2bce781 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -328,7 +328,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._willRestart = e.reason !== ShutdownReason.RELOAD; }); this._register(this.onDidStateChange(e => { - if ((this._willRestart || e.exitReason === TerminalExitReason.User) && e.taskId) { + if (e.kind === TaskEventKind.Changed) { + // no-op + } else if ((this._willRestart || (e.kind === TaskEventKind.Terminated && e.exitReason === TerminalExitReason.User)) && e.taskId) { this.removePersistentTask(e.taskId); } else if (e.kind === TaskEventKind.Start && e.__task && e.__task.getWorkspaceFolder()) { this._setPersistentTask(e.__task); diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index 27bdc71350a..8ab974d4cbe 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -145,7 +145,7 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench } private _ignoreEventForUpdateRunningTasksCount(event: ITaskEvent): boolean { - if (!this._taskService.inTerminal()) { + if (!this._taskService.inTerminal() || event.kind === TaskEventKind.Changed) { return false; } @@ -153,10 +153,6 @@ export class TaskStatusBarContributions extends Disposable implements IWorkbench return true; } - if (!event.__task) { - return false; - } - return event.__task.configurationProperties.problemMatchers === undefined || event.__task.configurationProperties.problemMatchers.length === 0; } } diff --git a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts index 176d0962ba2..b2331ad6bfd 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskTerminalStatus.ts @@ -8,7 +8,7 @@ import { Codicon } from 'vs/base/common/codicons'; 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 { ITaskEvent, TaskEventKind, TaskRunType } from 'vs/workbench/contrib/tasks/common/tasks'; +import { ITaskGeneralEvent, ITaskProcessEndedEvent, ITaskProcessStartedEvent, 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 { MarkerSeverity } from 'vs/platform/markers/common/markers'; @@ -71,14 +71,14 @@ export class TaskTerminalStatus extends Disposable { this.terminalMap.set(terminal.instanceId, { terminal, task, status, problemMatcher, taskRunEnded: false }); } - private terminalFromEvent(event: ITaskEvent): ITerminalData | undefined { - if (!event.terminalId) { + private terminalFromEvent(event: { terminalId: number | undefined }): ITerminalData | undefined { + if (!('terminalId' in event) || !event.terminalId) { return undefined; } return this.terminalMap.get(event.terminalId); } - private eventEnd(event: ITaskEvent) { + private eventEnd(event: ITaskProcessEndedEvent) { const terminalData = this.terminalFromEvent(event); if (!terminalData) { return; @@ -104,7 +104,7 @@ export class TaskTerminalStatus extends Disposable { } } - private eventInactive(event: ITaskEvent) { + private eventInactive(event: ITaskGeneralEvent) { const terminalData = this.terminalFromEvent(event); if (!terminalData || !terminalData.problemMatcher || terminalData.taskRunEnded) { return; @@ -123,7 +123,7 @@ export class TaskTerminalStatus extends Disposable { } } - private eventActive(event: ITaskEvent) { + private eventActive(event: ITaskGeneralEvent | ITaskProcessStartedEvent) { const terminalData = this.terminalFromEvent(event); if (!terminalData) { return; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index f10129a15c5..ab75308843d 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -423,7 +423,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } private _fireTaskEvent(event: ITaskEvent) { - if (event.__task) { + if (event.kind !== TaskEventKind.Changed) { const activeTask = this._activeTasks[event.__task.getMapKey()]; if (activeTask) { activeTask.state = event.kind; @@ -440,13 +440,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } return new Promise((resolve, reject) => { terminal.onDisposed(terminal => { - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Terminated, task, terminal.instanceId, terminal.exitReason)); + this._fireTaskEvent(TaskEvent.terminated(task, terminal.instanceId, terminal.exitReason)); }); const onExit = terminal.onExit(() => { const task = activeTerminal.task; try { onExit.dispose(); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Terminated, task, terminal.instanceId, terminal.exitReason)); + this._fireTaskEvent(TaskEvent.terminated(task, terminal.instanceId, terminal.exitReason)); } catch (error) { // Do nothing. } @@ -466,7 +466,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const task = terminalData.task; try { onExit.dispose(); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Terminated, task, terminal.instanceId, terminal.exitReason)); + this._fireTaskEvent(TaskEvent.terminated(task, terminal.instanceId, terminal.exitReason)); } catch (error) { // Do nothing. } @@ -520,7 +520,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } } if (!taskResult) { - this._fireTaskEvent(TaskEvent.create(TaskEventKind.DependsOnStarted, task)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.DependsOnStarted, task)); taskResult = this._executeDependencyTask(dependencyTask, resolver, trigger, nextLiveDependencies, encounteredTasks, alreadyResolved); } encounteredTasks.set(commonKey, taskResult); @@ -654,7 +654,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { private async _acquireInput(taskSystemInfo: ITaskSystemInfo | undefined, workspaceFolder: IWorkspaceFolder | undefined, task: CustomTask | ContributedTask, variables: Set, alreadyResolved: Map): Promise { const resolved = await this._resolveVariablesFromSet(taskSystemInfo, workspaceFolder, task, variables, alreadyResolved); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.AcquiredInput, task)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.AcquiredInput, task)); return resolved; } @@ -759,7 +759,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return this._executeInTerminal(task, trigger, new VariableResolver(workspaceFolder, systemInfo, resolvedVariables.variables, this._configurationResolverService), workspaceFolder); } else { // Allows the taskExecutions array to be updated in the extension host - this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.End, task)); return Promise.resolve({ exitCode: 0 }); } }, reason => { @@ -793,7 +793,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return this._acquireInput(lastTask.getVerifiedTask().systemInfo, lastTask.getVerifiedTask().workspaceFolder, task, variables, alreadyResolved).then((resolvedVariables) => { if (!resolvedVariables) { // Allows the taskExecutions array to be updated in the extension host - this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.End, task)); return { exitCode: 0 }; } this._currentTask.resolvedVariables = resolvedVariables; @@ -825,13 +825,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (event.kind === ProblemCollectorEventKind.BackgroundProcessingBegins) { eventCounter++; this._busyTasks[mapKey] = task; - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Active, task, terminal?.instanceId)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.Active, task, terminal?.instanceId)); } else if (event.kind === ProblemCollectorEventKind.BackgroundProcessingEnds) { eventCounter--; if (this._busyTasks[mapKey]) { delete this._busyTasks[mapKey]; } - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task, terminal?.instanceId)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.Inactive, task, terminal?.instanceId)); if (eventCounter === 0) { if ((watchingProblemMatcher.numberOfMatches > 0) && watchingProblemMatcher.maxMarkerSeverity && (watchingProblemMatcher.maxMarkerSeverity >= MarkerSeverity.Error)) { @@ -862,13 +862,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let processStartedSignaled = false; terminal.processReady.then(() => { if (!processStartedSignaled) { - this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.instanceId, terminal!.processId!)); + this._fireTaskEvent(TaskEvent.processStarted(task, terminal!.instanceId, terminal!.processId!)); processStartedSignaled = true; } }, (_error) => { this._logService.error('Task terminal process never got ready'); }); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Start, task, terminal.instanceId, resolver.values)); + this._fireTaskEvent(TaskEvent.start(task, terminal.instanceId, resolver.values)); let onData: IDisposable | undefined; if (problemMatchers.length) { // prevent https://github.com/microsoft/vscode/issues/174511 from happening @@ -894,7 +894,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { delete this._busyTasks[mapKey]; } this._removeFromActiveTasks(task); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); + this._fireTaskEvent(TaskEvent.changed()); if (terminalLaunchResult !== undefined) { // Only keep a reference to the terminal if it is not being disposed. switch (task.command.presentation!.panel) { @@ -920,16 +920,16 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { watchingProblemMatcher.done(); watchingProblemMatcher.dispose(); if (!processStartedSignaled) { - this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.instanceId, terminal!.processId!)); + this._fireTaskEvent(TaskEvent.processStarted(task, terminal!.instanceId, terminal!.processId!)); processStartedSignaled = true; } - this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessEnded, task, terminal!.instanceId, exitCode)); + this._fireTaskEvent(TaskEvent.processEnded(task, terminal!.instanceId, exitCode)); for (let i = 0; i < eventCounter; i++) { - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task, terminal!.instanceId)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.Inactive, task, terminal!.instanceId)); } eventCounter = 0; - this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.End, task)); toDispose.dispose(); resolve({ exitCode: exitCode ?? undefined }); }); @@ -962,16 +962,16 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { let processStartedSignaled = false; terminal.processReady.then(() => { if (!processStartedSignaled) { - this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal!.instanceId, terminal!.processId!)); + this._fireTaskEvent(TaskEvent.processStarted(task, terminal!.instanceId, terminal!.processId!)); processStartedSignaled = true; } }, (_error) => { // The process never got ready. Need to think how to handle this. }); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Start, task, terminal.instanceId, resolver.values)); + this._fireTaskEvent(TaskEvent.start(task, terminal.instanceId, resolver.values)); const mapKey = task.getMapKey(); this._busyTasks[mapKey] = task; - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Active, task, terminal.instanceId)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.Active, task, terminal.instanceId)); const problemMatchers = await this._resolveMatchers(resolver, task.configurationProperties.problemMatchers); const startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this._markerService, this._modelService, ProblemHandlingStrategy.Clean, this._fileService); this._terminalStatusManager.addTerminal(task, terminal, startStopProblemMatcher); @@ -984,7 +984,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { onExit.dispose(); const key = task.getMapKey(); this._removeFromActiveTasks(task); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); + this._fireTaskEvent(TaskEvent.changed()); if (terminalLaunchResult !== undefined) { // Only keep a reference to the terminal if it is not being disposed. switch (task.command.presentation!.panel) { @@ -1018,16 +1018,16 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { startStopProblemMatcher.dispose(); }, 100); if (!processStartedSignaled && terminal) { - this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessStarted, task, terminal.instanceId, terminal.processId!)); + this._fireTaskEvent(TaskEvent.processStarted(task, terminal.instanceId, terminal.processId!)); processStartedSignaled = true; } - this._fireTaskEvent(TaskEvent.create(TaskEventKind.ProcessEnded, task, terminal?.instanceId, exitCode ?? undefined)); + this._fireTaskEvent(TaskEvent.processEnded(task, terminal?.instanceId, exitCode ?? undefined)); if (this._busyTasks[mapKey]) { delete this._busyTasks[mapKey]; } - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Inactive, task, terminal?.instanceId)); - this._fireTaskEvent(TaskEvent.create(TaskEventKind.End, task, terminal?.instanceId)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.Inactive, task, terminal?.instanceId)); + this._fireTaskEvent(TaskEvent.general(TaskEventKind.End, task, terminal?.instanceId)); resolve({ exitCode: exitCode ?? undefined }); }); }); @@ -1041,7 +1041,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { this._terminalGroupService.showPanel(task.command.presentation.focus); } this._activeTasks[task.getMapKey()].terminal = terminal; - this._fireTaskEvent(TaskEvent.create(TaskEventKind.Changed)); + this._fireTaskEvent(TaskEvent.changed()); return promise; } @@ -1283,11 +1283,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { private async _doCreateTerminal(task: Task, group: string | undefined, launchConfigs: IShellLaunchConfig): Promise { const reconnectedTerminal = await this._reconnectToTerminal(task); + const onDisposed = (terminal: ITerminalInstance) => this._fireTaskEvent(TaskEvent.terminated(task, terminal.instanceId, terminal.exitReason)); if (reconnectedTerminal) { if ('command' in task && task.command.presentation) { reconnectedTerminal.waitOnExit = getWaitOnExitValue(task.command.presentation, task.configurationProperties); } - reconnectedTerminal.onDisposed((terminal) => this._fireTaskEvent({ kind: TaskEventKind.Terminated, exitReason: terminal.exitReason, taskId: task.getRecentlyUsedKey() })); + reconnectedTerminal.onDisposed(onDisposed); this._logService.trace('reconnected to task and terminal', task._id); return reconnectedTerminal; } @@ -1299,7 +1300,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { this._logService.trace(`Found terminal to split for group ${group}`); const originalInstance = terminal.terminal; const result = await this._terminalService.createTerminal({ location: { parentTerminal: originalInstance }, config: launchConfigs }); - result.onDisposed((terminal) => this._fireTaskEvent({ kind: TaskEventKind.Terminated, exitReason: terminal.exitReason, taskId: task.getRecentlyUsedKey() })); + result.onDisposed(onDisposed); if (result) { return result; } @@ -1309,7 +1310,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { } // Either no group is used, no terminal with the group exists or splitting an existing terminal failed. const createdTerminal = await this._terminalService.createTerminal({ config: launchConfigs }); - createdTerminal.onDisposed((terminal) => this._fireTaskEvent({ kind: TaskEventKind.Terminated, exitReason: terminal.exitReason, taskId: task.getRecentlyUsedKey() })); + createdTerminal.onDisposed(onDisposed); return createdTerminal; } diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 7e45e328082..fded77d7f8d 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -1116,20 +1116,55 @@ export const enum TaskRunType { Background = 'background' } -export interface ITaskEvent { - kind: TaskEventKind; - taskId?: string; - taskName?: string; - runType?: TaskRunType; - group?: string | TaskGroup; - processId?: number; - exitCode?: number; - terminalId?: number; - __task?: Task; - resolvedVariables?: Map; - exitReason?: TerminalExitReason; +export interface ITaskChangedEvent { + kind: TaskEventKind.Changed; } +interface ITaskCommon { + taskId: string; + runType: TaskRunType; + taskName: string | undefined; + group: string | TaskGroup | undefined; + __task: Task; +} + +export interface ITaskProcessStartedEvent extends ITaskCommon { + kind: TaskEventKind.ProcessStarted; + terminalId: number; + processId: number; +} + +export interface ITaskProcessEndedEvent extends ITaskCommon { + kind: TaskEventKind.ProcessEnded; + terminalId: number | undefined; + exitCode?: number; +} + +export interface ITaskTerminatedEvent extends ITaskCommon { + kind: TaskEventKind.Terminated; + terminalId: number; + exitReason: TerminalExitReason | undefined; +} + +export interface ITaskStartedEvent extends ITaskCommon { + kind: TaskEventKind.Start; + terminalId: number; + resolvedVariables: Map; +} + +export interface ITaskGeneralEvent extends ITaskCommon { + kind: TaskEventKind.AcquiredInput | TaskEventKind.DependsOnStarted | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.End; + terminalId: number | undefined; +} + +export type ITaskEvent = + | ITaskChangedEvent + | ITaskProcessStartedEvent + | ITaskProcessEndedEvent + | ITaskTerminatedEvent + | ITaskStartedEvent + | ITaskGeneralEvent; + export const enum TaskRunSource { System, User, @@ -1139,34 +1174,61 @@ export const enum TaskRunSource { } export namespace TaskEvent { - export function create(kind: TaskEventKind.ProcessStarted | TaskEventKind.ProcessEnded, task: Task, terminalId?: number, processIdOrExitCode?: number): ITaskEvent; - export function create(kind: TaskEventKind.Start, task: Task, terminalId?: number, resolvedVariables?: Map): ITaskEvent; - export function create(kind: TaskEventKind.AcquiredInput | TaskEventKind.DependsOnStarted | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.Terminated | TaskEventKind.End, task: Task, terminalId?: number, exitReason?: TerminalExitReason): ITaskEvent; - export function create(kind: TaskEventKind.Changed): ITaskEvent; - export function create(kind: TaskEventKind, task?: Task, terminalId?: number, resolvedVariablesORProcessIdOrExitCodeOrExitReason?: number | Map | TerminalExitReason): ITaskEvent { - if (task) { - const result: ITaskEvent = { - kind: kind, - taskId: task._id, - taskName: task.configurationProperties.name, - runType: task.configurationProperties.isBackground ? TaskRunType.Background : TaskRunType.SingleRun, - group: task.configurationProperties.group, - processId: undefined as number | undefined, - exitCode: undefined as number | undefined, - terminalId, - __task: task - }; - if (kind === TaskEventKind.Start) { - result.resolvedVariables = resolvedVariablesORProcessIdOrExitCodeOrExitReason as Map; - } else if (kind === TaskEventKind.ProcessStarted) { - result.processId = resolvedVariablesORProcessIdOrExitCodeOrExitReason as number; - } else if (kind === TaskEventKind.ProcessEnded) { - result.exitCode = resolvedVariablesORProcessIdOrExitCodeOrExitReason as number; - } - return Object.freeze(result); - } else { - return Object.freeze({ kind: TaskEventKind.Changed }); - } + function common(task: Task): ITaskCommon { + return { + taskId: task._id, + taskName: task.configurationProperties.name, + runType: task.configurationProperties.isBackground ? TaskRunType.Background : TaskRunType.SingleRun, + group: task.configurationProperties.group, + __task: task, + }; + } + + export function start(task: Task, terminalId: number, resolvedVariables: Map): ITaskStartedEvent { + return { + ...common(task), + kind: TaskEventKind.Start, + terminalId, + resolvedVariables, + }; + } + + export function processStarted(task: Task, terminalId: number, processId: number): ITaskProcessStartedEvent { + return { + ...common(task), + kind: TaskEventKind.ProcessStarted, + terminalId, + processId, + }; + } + export function processEnded(task: Task, terminalId: number | undefined, exitCode: number | undefined): ITaskProcessEndedEvent { + return { + ...common(task), + kind: TaskEventKind.ProcessEnded, + terminalId, + exitCode, + }; + } + + export function terminated(task: Task, terminalId: number, exitReason: TerminalExitReason | undefined): ITaskTerminatedEvent { + return { + ...common(task), + kind: TaskEventKind.Terminated, + exitReason, + terminalId, + }; + } + + export function general(kind: TaskEventKind.AcquiredInput | TaskEventKind.DependsOnStarted | TaskEventKind.Active | TaskEventKind.Inactive | TaskEventKind.End, task: Task, terminalId?: number): ITaskGeneralEvent { + return { + ...common(task), + kind, + terminalId, + }; + } + + export function changed(): ITaskChangedEvent { + return { kind: TaskEventKind.Changed }; } } diff --git a/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts b/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts index 39da6960b72..1e3880420fd 100644 --- a/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts +++ b/src/vs/workbench/contrib/tasks/test/browser/taskTerminalStatus.test.ts @@ -21,8 +21,8 @@ class TestTaskService implements Partial { public get onDidStateChange(): Event { return this._onDidStateChange.event; } - public triggerStateChange(event: ITaskEvent): void { - this._onDidStateChange.fire(event); + public triggerStateChange(event: Partial): void { + this._onDidStateChange.fire(event as ITaskEvent); } } @@ -85,7 +85,7 @@ suite('Task Terminal Status', () => { assertStatus(testTerminal.statusList, ACTIVE_TASK_STATUS); taskService.triggerStateChange({ kind: TaskEventKind.Inactive }); assertStatus(testTerminal.statusList, SUCCEEDED_TASK_STATUS); - taskService.triggerStateChange({ kind: TaskEventKind.End, exitCode: 2 }); + taskService.triggerStateChange({ kind: TaskEventKind.End }); 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', () => { From f46fe189d96a61784c0627aacc257ccd624986e2 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Wed, 6 Sep 2023 11:45:34 -0700 Subject: [PATCH 569/607] Tweak inline actions padding (#192322) --- src/vs/workbench/contrib/inlineChat/browser/inlineChat.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css index 87f586d2f02..cc4c61f55a8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.css @@ -106,7 +106,7 @@ } .monaco-editor .inline-chat .status.actions { - margin-top: 6px; + margin-top: 4px; } .monaco-editor .inline-chat .status .actions.hidden { From 932a8381e178016192e4f29a1d69f577d283976e Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 6 Sep 2023 15:43:04 -0400 Subject: [PATCH 570/607] fix context key issues --- .../chat/browser/actions/chatAccessibilityHelp.ts | 9 +++++---- .../contrib/chat/browser/actions/chatActions.ts | 9 +++------ .../contrib/inlineChat/browser/inlineChatActions.ts | 5 +++-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index e277b372021..8afefc2ca98 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -56,13 +56,12 @@ function descriptionForCommand(commandId: string, msg: string, noKbMsg: string, return format(noKbMsg, commandId); } -export async function runAccessibilityHelpAction(accessor: ServicesAccessor, editor: ICodeEditor, type: 'panelChat' | 'inlineChat'): Promise { +export async function runAccessibilityHelpAction(accessor: ServicesAccessor, editor: ICodeEditor | undefined, type: 'panelChat' | 'inlineChat'): Promise { const widgetService = accessor.get(IChatWidgetService); const accessibleViewService = accessor.get(IAccessibleViewService); const inputEditor: ICodeEditor | undefined = type === 'panelChat' ? widgetService.lastFocusedWidget?.inputEditor : editor; - const editorUri = editor.getModel()?.uri; - if (!inputEditor || !editorUri) { + if (!inputEditor) { return; } const domNode = inputEditor.getDomNode() ?? undefined; @@ -81,7 +80,9 @@ export async function runAccessibilityHelpAction(accessor: ServicesAccessor, edi inputEditor.setPosition(cachedPosition); inputEditor.focus(); } else if (type === 'inlineChat') { - InlineChatController.get(editor)?.focus(); + if (editor) { + InlineChatController.get(editor)?.focus(); + } } }, options: { type: AccessibleViewType.Help } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 8b783da509f..ca3c43e92d6 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -23,7 +23,7 @@ import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor'; import { ChatEditorInput } from 'vs/workbench/contrib/chat/browser/chatEditorInput'; import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane'; -import { CONTEXT_IN_CHAT_INPUT, CONTEXT_IN_CHAT_SESSION, CONTEXT_PROVIDER_EXISTS } from 'vs/workbench/contrib/chat/common/chatContextKeys'; +import { CONTEXT_IN_CHAT_INPUT, CONTEXT_IN_CHAT_SESSION, CONTEXT_PROVIDER_EXISTS, CONTEXT_REQUEST, CONTEXT_RESPONSE } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatDetail, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatWidgetHistoryService } from 'vs/workbench/contrib/chat/common/chatWidgetHistoryService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -107,11 +107,8 @@ export function registerChatActions() { super(); this._register(AccessibilityHelpAction.addImplementation(105, 'panelChat', async accessor => { const codeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor() || accessor.get(ICodeEditorService).getFocusedCodeEditor(); - if (!codeEditor) { - return; - } - runAccessibilityHelpAction(accessor, codeEditor, 'panelChat'); - }, CONTEXT_IN_CHAT_SESSION)); + runAccessibilityHelpAction(accessor, codeEditor ?? undefined, 'panelChat'); + }, ContextKeyExpr.or(CONTEXT_IN_CHAT_SESSION, CONTEXT_RESPONSE, CONTEXT_REQUEST))); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 05bb019ec59..82234ce0217 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -10,7 +10,7 @@ import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { EmbeddedCodeEditorWidget, EmbeddedDiffEditorWidget2 } from 'vs/editor/browser/widget/embeddedCodeEditorWidget'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { InlineChatController, InlineChatRunOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, InlineChatResponseType, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChateResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, MENU_INLINE_CHAT_WIDGET_TOGGLE, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_INNER_CURSOR_END } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_HAS_PROVIDER, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_EMPTY, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, CTX_INLINE_CHAT_VISIBLE, MENU_INLINE_CHAT_WIDGET, MENU_INLINE_CHAT_WIDGET_DISCARD, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_LAST_FEEDBACK, CTX_INLINE_CHAT_EDIT_MODE, EditMode, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, MENU_INLINE_CHAT_WIDGET_MARKDOWN_MESSAGE, CTX_INLINE_CHAT_MESSAGE_CROP_STATE, CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, MENU_INLINE_CHAT_WIDGET_FEEDBACK, ACTION_ACCEPT_CHANGES, ACTION_REGENERATE_RESPONSE, InlineChatResponseType, CTX_INLINE_CHAT_RESPONSE_TYPES, InlineChateResponseTypes, ACTION_VIEW_IN_CHAT, CTX_INLINE_CHAT_USER_DID_EDIT, MENU_INLINE_CHAT_WIDGET_TOGGLE, CTX_INLINE_CHAT_INNER_CURSOR_START, CTX_INLINE_CHAT_INNER_CURSOR_END, CTX_INLINE_CHAT_RESPONSE_FOCUSED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { localize } from 'vs/nls'; import { IAction2Options, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -32,6 +32,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { Position } from 'vs/editor/common/core/position'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; +import { CONTEXT_REQUEST } from 'vs/workbench/contrib/chat/common/chatContextKeys'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); @@ -643,6 +644,6 @@ export class InlineAccessibilityHelpContribution extends Disposable { return; } runAccessibilityHelpAction(accessor, codeEditor, 'inlineChat'); - }, CTX_INLINE_CHAT_FOCUSED)); + }, ContextKeyExpr.or(CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_FOCUSED, CONTEXT_REQUEST))); } } From 0a49cea8392374e529bdcf3d25e33036c6b9cba4 Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 6 Sep 2023 15:46:41 -0400 Subject: [PATCH 571/607] rm something --- .../workbench/contrib/inlineChat/browser/inlineChatActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 82234ce0217..87658b2544d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -644,6 +644,6 @@ export class InlineAccessibilityHelpContribution extends Disposable { return; } runAccessibilityHelpAction(accessor, codeEditor, 'inlineChat'); - }, ContextKeyExpr.or(CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_FOCUSED, CONTEXT_REQUEST))); + }, ContextKeyExpr.or(CTX_INLINE_CHAT_RESPONSE_FOCUSED, CTX_INLINE_CHAT_FOCUSED))); } } From 80becd6bc31f820d4610bbe4a68ded1b6a703acc Mon Sep 17 00:00:00 2001 From: meganrogge Date: Wed, 6 Sep 2023 15:46:41 -0400 Subject: [PATCH 572/607] rm something --- src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 87658b2544d..d78ba2b101d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -32,7 +32,6 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { Position } from 'vs/editor/common/core/position'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { AccessibilityHelpAction } from 'vs/workbench/contrib/accessibility/browser/accessibleViewActions'; -import { CONTEXT_REQUEST } from 'vs/workbench/contrib/chat/common/chatContextKeys'; CommandsRegistry.registerCommandAlias('interactiveEditor.start', 'inlineChat.start'); From a06b555357126809dd58a0f5cb72912cd74a11d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 6 Sep 2023 12:58:09 -0700 Subject: [PATCH 573/607] Terminal test leak progress --- .../common/environmentVariableService.ts | 2 +- .../contrib/terminal/common/history.ts | 4 ++-- .../environmentVariableCollection.test.ts | 3 +++ .../common/environmentVariableService.test.ts | 19 ++++++++--------- .../common/environmentVariableShared.test.ts | 5 +++++ .../terminal/test/common/history.test.ts | 11 ++++++---- .../test/common/terminalColorRegistry.test.ts | 2 ++ .../test/common/terminalDataBuffering.test.ts | 19 ++++++++++------- .../test/common/terminalEnvironment.test.ts | 3 +++ .../test/node/terminalProfiles.test.ts | 3 +++ .../test/browser/bufferContentTracker.test.ts | 21 ++++++++++--------- .../links/browser/terminalLinkManager.ts | 8 +++---- .../test/browser/terminalLinkHelpers.test.ts | 3 +++ .../test/browser/terminalLinkManager.test.ts | 19 ++++++++--------- 14 files changed, 73 insertions(+), 49 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts index 0aaa48f6b05..ddb64c1f0b8 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariableService.ts @@ -56,7 +56,7 @@ export class EnvironmentVariableService extends Disposable implements IEnvironme this.mergedCollection = this._resolveMergedCollection(); // Listen for uninstalled/disabled extensions - this._extensionService.onDidChangeExtensions(() => this._invalidateExtensionCollections()); + this._register(this._extensionService.onDidChangeExtensions(() => this._invalidateExtensionCollections())); } set(extensionIdentifier: string, collection: IEnvironmentVariableCollectionWithPersistence): void { diff --git a/src/vs/workbench/contrib/terminal/common/history.ts b/src/vs/workbench/contrib/terminal/common/history.ts index 1f82b4d0640..cf0d940e716 100644 --- a/src/vs/workbench/contrib/terminal/common/history.ts +++ b/src/vs/workbench/contrib/terminal/common/history.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { env } from 'vs/base/common/process'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { LRUCache } from 'vs/base/common/map'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { FileOperationError, FileOperationResult, IFileContent, IFileService } from 'vs/platform/files/common/files'; @@ -135,7 +135,7 @@ export class TerminalPersistedHistory extends Disposable implements ITerminal })); // Listen to cache changes from other windows - this._register(this._storageService.onDidChangeValue(StorageScope.APPLICATION, this._getTimestampStorageKey(), this._register(new DisposableStore()))(() => { + this._register(this._storageService.onDidChangeValue(StorageScope.APPLICATION, this._getTimestampStorageKey(), this._store)(() => { if (!this._isStale) { this._isStale = this._storageService.getNumber(this._getTimestampStorageKey(), StorageScope.APPLICATION, 0) !== this._timestamp; } diff --git a/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts b/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts index cb047ca042b..3367f87854f 100644 --- a/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts @@ -9,8 +9,11 @@ import { IProcessEnvironment, isWindows } from 'vs/base/common/platform'; import { MergedEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableCollection'; import { deserializeEnvironmentDescriptionMap, deserializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('ctor', () => { test('Should keep entries that come after a Prepend or Append type mutators', () => { const merged = new MergedEnvironmentVariableCollection(new Map([ diff --git a/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts b/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts index e338f9d55fd..631a0b7bc86 100644 --- a/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/environmentVariableService.test.ts @@ -14,6 +14,7 @@ import { Emitter } from 'vs/base/common/event'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestEnvironmentVariableService extends EnvironmentVariableService { persistCollections(): void { this._persistCollections(); } @@ -21,6 +22,8 @@ class TestEnvironmentVariableService extends EnvironmentVariableService { } suite('EnvironmentVariable - EnvironmentVariableService', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let environmentVariableService: TestEnvironmentVariableService; let storageService: TestStorageService; @@ -28,27 +31,23 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => { let changeExtensionsEvent: Emitter; setup(() => { - changeExtensionsEvent = new Emitter(); + changeExtensionsEvent = store.add(new Emitter()); - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); instantiationService.stub(IExtensionService, TestExtensionService); - storageService = new TestStorageService(); + storageService = store.add(new TestStorageService()); historyService = new TestHistoryService(); instantiationService.stub(IStorageService, storageService); instantiationService.stub(IExtensionService, TestExtensionService); instantiationService.stub(IExtensionService, 'onDidChangeExtensions', changeExtensionsEvent.event); - instantiationService.stub(IExtensionService, 'getExtensions', [ + instantiationService.stub(IExtensionService, 'extensions', [ { identifier: { value: 'ext1' } }, { identifier: { value: 'ext2' } }, { identifier: { value: 'ext3' } } ]); instantiationService.stub(IHistoryService, historyService); - environmentVariableService = instantiationService.createInstance(TestEnvironmentVariableService); - }); - - teardown(() => { - instantiationService.dispose(); + environmentVariableService = store.add(instantiationService.createInstance(TestEnvironmentVariableService)); }); test('should persist collections to the storage service and be able to restore from them', () => { @@ -65,7 +64,7 @@ suite('EnvironmentVariable - EnvironmentVariableService', () => { // Persist with old service, create a new service with the same storage service to verify restore environmentVariableService.persistCollections(); - const service2: TestEnvironmentVariableService = instantiationService.createInstance(TestEnvironmentVariableService); + const service2: TestEnvironmentVariableService = store.add(instantiationService.createInstance(TestEnvironmentVariableService)); deepStrictEqual([...service2.mergedCollection.getVariableMap(undefined).entries()], [ ['A', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Replace, value: 'a', variable: 'A', options: undefined }]], ['B', [{ extensionIdentifier: 'ext1', type: EnvironmentVariableMutatorType.Append, value: 'b', variable: 'B', options: undefined }]], diff --git a/src/vs/workbench/contrib/terminal/test/common/environmentVariableShared.test.ts b/src/vs/workbench/contrib/terminal/test/common/environmentVariableShared.test.ts index d3fb2177556..ba72290399b 100644 --- a/src/vs/workbench/contrib/terminal/test/common/environmentVariableShared.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/environmentVariableShared.test.ts @@ -6,8 +6,11 @@ import { deepStrictEqual } from 'assert'; import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/platform/terminal/common/environmentVariableShared'; import { EnvironmentVariableMutatorType, IEnvironmentVariableMutator } from 'vs/platform/terminal/common/environmentVariable'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('EnvironmentVariable - deserializeEnvironmentVariableCollection', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('should construct correctly with 3 arguments', () => { const c = deserializeEnvironmentVariableCollection([ ['A', { value: 'a', type: EnvironmentVariableMutatorType.Replace, variable: 'A' }], @@ -23,6 +26,8 @@ suite('EnvironmentVariable - deserializeEnvironmentVariableCollection', () => { }); suite('EnvironmentVariable - serializeEnvironmentVariableCollection', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('should correctly serialize the object', () => { const collection = new Map(); deepStrictEqual(serializeEnvironmentVariableCollection(collection), []); diff --git a/src/vs/workbench/contrib/terminal/test/common/history.test.ts b/src/vs/workbench/contrib/terminal/test/common/history.test.ts index 428ec07e20f..db0777ddd08 100644 --- a/src/vs/workbench/contrib/terminal/test/common/history.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/history.test.ts @@ -10,6 +10,7 @@ import { join } from 'vs/base/common/path'; import { isWindows, OperatingSystem } from 'vs/base/common/platform'; import { env } from 'vs/base/common/process'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IFileService } from 'vs/platform/files/common/files'; @@ -40,6 +41,8 @@ const expectedCommands = [ ]; suite('Terminal history', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + suite('TerminalPersistedHistory', () => { let history: ITerminalPersistedHistory; let instantiationService: TestInstantiationService; @@ -48,12 +51,12 @@ suite('Terminal history', () => { setup(() => { configurationService = new TestConfigurationService(getConfig(5)); - storageService = new TestStorageService(); - instantiationService = new TestInstantiationService(); + storageService = store.add(new TestStorageService()); + instantiationService = store.add(new TestInstantiationService()); instantiationService.set(IConfigurationService, configurationService); instantiationService.set(IStorageService, storageService); - history = instantiationService.createInstance(TerminalPersistedHistory, 'test'); + history = store.add(instantiationService.createInstance(TerminalPersistedHistory, 'test')); }); teardown(() => { @@ -116,7 +119,7 @@ suite('Terminal history', () => { history.add('2', 2); history.add('3', 3); strictEqual(Array.from(history.entries).length, 3); - const history2 = instantiationService.createInstance(TerminalPersistedHistory, 'test'); + const history2 = store.add(instantiationService.createInstance(TerminalPersistedHistory, 'test')); strictEqual(Array.from(history2.entries).length, 3); }); }); diff --git a/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts b/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts index 63c258c9b8e..4a9d9a78954 100644 --- a/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/terminalColorRegistry.test.ts @@ -10,6 +10,7 @@ import { ansiColorIdentifiers, registerColors } from 'vs/workbench/contrib/termi import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { Color } from 'vs/base/common/color'; import { ColorScheme } from 'vs/platform/theme/common/theme'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; registerColors(); @@ -29,6 +30,7 @@ function getMockTheme(type: ColorScheme): IColorTheme { } suite('Workbench - TerminalColorRegistry', () => { + ensureNoDisposablesAreLeakedInTestSuite(); test('hc colors', function () { const theme = getMockTheme(ColorScheme.HIGH_CONTRAST_DARK); diff --git a/src/vs/workbench/contrib/terminal/test/common/terminalDataBuffering.test.ts b/src/vs/workbench/contrib/terminal/test/common/terminalDataBuffering.test.ts index 01cec8ee267..dd74c586060 100644 --- a/src/vs/workbench/contrib/terminal/test/common/terminalDataBuffering.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/terminalDataBuffering.test.ts @@ -5,11 +5,14 @@ import * as assert from 'assert'; import { Emitter } from 'vs/base/common/event'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TerminalDataBufferer } from 'vs/platform/terminal/common/terminalDataBuffering'; const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); suite('Workbench - TerminalDataBufferer', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let bufferer: TerminalDataBufferer; let counter: { [id: number]: number }; let data: { [id: number]: string }; @@ -17,7 +20,7 @@ suite('Workbench - TerminalDataBufferer', () => { setup(async () => { counter = {}; data = {}; - bufferer = new TerminalDataBufferer((id, e) => { + bufferer = store.add(new TerminalDataBufferer((id, e) => { if (!(id in counter)) { counter[id] = 0; } @@ -26,13 +29,13 @@ suite('Workbench - TerminalDataBufferer', () => { data[id] = ''; } data[id] = e; - }); + })); }); test('start', async () => { const terminalOnData = new Emitter(); - bufferer.startBuffering(1, terminalOnData.event, 0); + store.add(bufferer.startBuffering(1, terminalOnData.event, 0)); terminalOnData.fire('1'); terminalOnData.fire('2'); @@ -55,8 +58,8 @@ suite('Workbench - TerminalDataBufferer', () => { const terminal1OnData = new Emitter(); const terminal2OnData = new Emitter(); - bufferer.startBuffering(1, terminal1OnData.event, 0); - bufferer.startBuffering(2, terminal2OnData.event, 0); + store.add(bufferer.startBuffering(1, terminal1OnData.event, 0)); + store.add(bufferer.startBuffering(2, terminal2OnData.event, 0)); terminal1OnData.fire('1'); terminal2OnData.fire('4'); @@ -100,7 +103,7 @@ suite('Workbench - TerminalDataBufferer', () => { const terminal2OnData = new Emitter(); bufferer.startBuffering(1, terminal1OnData.event, 0); - bufferer.startBuffering(2, terminal2OnData.event, 0); + store.add(bufferer.startBuffering(2, terminal2OnData.event, 0)); terminal1OnData.fire('1'); terminal2OnData.fire('4'); @@ -128,8 +131,8 @@ suite('Workbench - TerminalDataBufferer', () => { const terminal1OnData = new Emitter(); const terminal2OnData = new Emitter(); - bufferer.startBuffering(1, terminal1OnData.event, 0); - bufferer.startBuffering(2, terminal2OnData.event, 0); + store.add(bufferer.startBuffering(1, terminal1OnData.event, 0)); + store.add(bufferer.startBuffering(2, terminal2OnData.event, 0)); terminal1OnData.fire('1'); terminal2OnData.fire('4'); diff --git a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts index 2fcf2ecf33c..6c7ae16402a 100644 --- a/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/terminalEnvironment.test.ts @@ -9,8 +9,11 @@ import { isWindows, OperatingSystem } from 'vs/base/common/platform'; import { URI as Uri } from 'vs/base/common/uri'; import { addTerminalEnvironmentKeys, createTerminalEnvironment, getCwd, getLangEnvVariable, mergeEnvironments, preparePathForShell, shouldSetLangEnvVariable } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { PosixShellType, WindowsShellType } from 'vs/platform/terminal/common/terminal'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Workbench - TerminalEnvironment', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('addTerminalEnvironmentKeys', () => { test('should set expected variables', () => { const env: { [key: string]: any } = {}; diff --git a/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts b/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts index 5e6f6e31c3f..d86818ff870 100644 --- a/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts +++ b/src/vs/workbench/contrib/terminal/test/node/terminalProfiles.test.ts @@ -9,6 +9,7 @@ import { ITerminalProfile, ProfileSource } from 'vs/platform/terminal/common/ter import { ITerminalConfiguration, ITerminalProfiles } from 'vs/workbench/contrib/terminal/common/terminal'; import { detectAvailableProfiles, IFsProvider } from 'vs/platform/terminal/node/terminalProfiles'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; /** * Assets that two profiles objects are equal, this will treat explicit undefined and unset @@ -28,6 +29,8 @@ function profilesEqual(actualProfiles: ITerminalProfile[], expectedProfiles: ITe } suite('Workbench - TerminalProfiles', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('detectAvailableProfiles', () => { if (isWindows) { test('should detect Git Bash and provide login args', async () => { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index 240c15c4c21..d494ce0a93d 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { importAMDNodeModule } from 'vs/amdX'; import { isWindows } from 'vs/base/common/platform'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -41,6 +42,8 @@ const defaultTerminalConfig: Partial = { }; suite('Buffer Content Tracker', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let themeService: TestThemeService; @@ -50,33 +53,31 @@ suite('Buffer Content Tracker', () => { let bufferTracker: BufferContentTracker; const prompt = 'vscode-git:(prompt/more-tests)'; const promptPlusData = 'vscode-git:(prompt/more-tests) ' + 'some data'; + setup(async () => { configurationService = new TestConfigurationService({ terminal: { integrated: defaultTerminalConfig } }); - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); themeService = new TestThemeService(); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IThemeService, themeService); instantiationService.stub(ITerminalLogService, new NullLogService()); - instantiationService.stub(ILoggerService, new TestLoggerService()); - instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); + instantiationService.stub(ILoggerService, store.add(new TestLoggerService())); + instantiationService.stub(IContextMenuService, store.add(instantiationService.createInstance(ContextMenuService))); instantiationService.stub(ILifecycleService, new TestLifecycleService()); instantiationService.stub(IContextKeyService, new MockContextKeyService()); - configHelper = instantiationService.createInstance(TerminalConfigHelper); - capabilities = new TerminalCapabilityStore(); + configHelper = store.add(instantiationService.createInstance(TerminalConfigHelper)); + capabilities = store.add(new TerminalCapabilityStore()); if (!isWindows) { capabilities.add(TerminalCapability.NaiveCwdDetection, null!); } const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - xterm = instantiationService.createInstance(XtermTerminal, TerminalCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, capabilities, '', new MockContextKeyService().createKey('', true)!, true); + xterm = store.add(instantiationService.createInstance(XtermTerminal, TerminalCtor, configHelper, 80, 30, { getBackgroundColor: () => undefined }, capabilities, '', new MockContextKeyService().createKey('', true)!, true)); const container = document.createElement('div'); xterm.raw.open(container); configurationService = new TestConfigurationService({ terminal: { integrated: { tabs: { separator: ' - ', title: '${cwd}', description: '${cwd}' } } } }); - configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!); bufferTracker = instantiationService.createInstance(BufferContentTracker, xterm); }); - teardown(() => { - instantiationService.dispose(); - }); + test('should not clear the prompt line', async () => { assert.strictEqual(bufferTracker.lines.length, 0); await writeP(xterm.raw, prompt); diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts index 93105d6e3c0..bdf2d680d99 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkManager.ts @@ -132,8 +132,8 @@ export class TerminalLinkManager extends DisposableStore { } private _setupLinkDetector(id: string, detector: ITerminalLinkDetector, isExternal: boolean = false): ILinkProvider { - const detectorAdapter = this._instantiationService.createInstance(TerminalLinkDetectorAdapter, detector); - detectorAdapter.onDidActivateLink(e => { + const detectorAdapter = this.add(this._instantiationService.createInstance(TerminalLinkDetectorAdapter, detector)); + this.add(detectorAdapter.onDidActivateLink(e => { // Prevent default electron link handling so Alt+Click mode works normally e.event?.preventDefault(); // Require correct modifier on click unless event is coming from linkQuickPick selection @@ -147,8 +147,8 @@ export class TerminalLinkManager extends DisposableStore { } else { this._openLink(e.link); } - }); - detectorAdapter.onDidShowHover(e => this._tooltipCallback(e.link, e.viewportRange, e.modifierDownCallback, e.modifierUpCallback)); + })); + this.add(detectorAdapter.onDidShowHover(e => this._tooltipCallback(e.link, e.viewportRange, e.modifierDownCallback, e.modifierUpCallback))); if (!isExternal) { this._standardLinkProviders.set(id, detectorAdapter); } diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts index 32bc485b448..bf12083f0fe 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkHelpers.test.ts @@ -6,8 +6,11 @@ import * as assert from 'assert'; import type { IBufferLine, IBufferCell } from 'xterm'; import { convertLinkRangeToBuffer } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Workbench - Terminal Link Helpers', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('convertLinkRangeToBuffer', () => { test('should convert ranges for ascii characters', () => { const lines = createBufferLineArray([ diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts index 884214a69e5..d40bbb8a085 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkManager.test.ts @@ -24,6 +24,7 @@ import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServic import type { ILink, Terminal } from 'xterm'; import { TerminalLinkResolver } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkResolver'; import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const defaultTerminalConfig: Partial = { fontFamily: 'monospace', @@ -55,6 +56,8 @@ class TestLinkManager extends TerminalLinkManager { } suite('TerminalLinkManager', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let themeService: TestThemeService; @@ -75,17 +78,17 @@ suite('TerminalLinkManager', () => { themeService = new TestThemeService(); viewDescriptorService = new TestViewDescriptorService(); - instantiationService = new TestInstantiationService(); - instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); + instantiationService = store.add(new TestInstantiationService()); + instantiationService.stub(IContextMenuService, store.add(instantiationService.createInstance(ContextMenuService))); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(ILogService, new NullLogService()); - instantiationService.stub(IStorageService, new TestStorageService()); + instantiationService.stub(IStorageService, store.add(new TestStorageService())); instantiationService.stub(IThemeService, themeService); instantiationService.stub(IViewDescriptorService, viewDescriptorService); const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); - linkManager = instantiationService.createInstance(TestLinkManager, xterm, upcastPartial({ + xterm = store.add(new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 })); + linkManager = store.add(instantiationService.createInstance(TestLinkManager, xterm, upcastPartial({ get initialCwd() { return ''; } @@ -93,11 +96,7 @@ suite('TerminalLinkManager', () => { get(capability: T): ITerminalCapabilityImplMap[T] | undefined { return undefined; } - } as Partial as any, instantiationService.createInstance(TerminalLinkResolver)); - }); - - teardown(() => { - instantiationService.dispose(); + } as Partial as any, instantiationService.createInstance(TerminalLinkResolver))); }); suite('getLinks and open recent link', () => { From efc1b30976f4a8924157949e292f1bf46aff8d1e Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 6 Sep 2023 13:27:16 -0700 Subject: [PATCH 574/607] Add custom Node option to run TS Server (#191019) * Add config for running tsserver on custom node * log when custom node install is used * create node version manager * get node path from node version manager everywhere * modify prompt * fix useIpc * use spawn for custom node and set windowsHide * detect node * link memory setting to node setting --- .../typescript-language-features/package.json | 10 +- .../package.nls.json | 3 +- .../configuration/configuration.browser.ts | 9 ++ .../configuration/configuration.electron.ts | 64 ++++++++ .../src/configuration/configuration.ts | 6 + .../src/tsServer/nodeManager.ts | 149 ++++++++++++++++++ .../src/tsServer/server.ts | 2 + .../src/tsServer/serverProcess.browser.ts | 2 + .../src/tsServer/serverProcess.electron.ts | 34 ++-- .../src/tsServer/spawner.ts | 4 +- .../src/typescriptServiceClient.ts | 15 +- 11 files changed, 283 insertions(+), 15 deletions(-) create mode 100644 extensions/typescript-language-features/src/tsServer/nodeManager.ts diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 38730118883..eda688b9a93 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -21,7 +21,8 @@ "restrictedConfigurations": [ "typescript.tsdk", "typescript.tsserver.pluginPaths", - "typescript.npm" + "typescript.npm", + "typescript.tsserver.nodePath" ] } }, @@ -1132,7 +1133,7 @@ "typescript.tsserver.maxTsServerMemory": { "type": "number", "default": 3072, - "description": "%configuration.tsserver.maxTsServerMemory%", + "markdownDescription": "%configuration.tsserver.maxTsServerMemory%", "scope": "window" }, "typescript.tsserver.experimental.enableProjectDiagnostics": { @@ -1251,6 +1252,11 @@ "description": "%configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors%", "scope": "window" }, + "typescript.tsserver.nodePath": { + "type": "string", + "description": "%configuration.tsserver.nodePath%", + "scope": "window" + }, "typescript.experimental.tsserver.web.typeAcquisition.enabled": { "type": "boolean", "default": false, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 641a4092870..c235219fef2 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -70,7 +70,7 @@ "configuration.tsserver.useSyntaxServer.always": "Use a lighter weight syntax server to handle all IntelliSense operations. This syntax server can only provide IntelliSense for opened files.", "configuration.tsserver.useSyntaxServer.never": "Don't use a dedicated syntax server. Use a single server to handle all IntelliSense operations.", "configuration.tsserver.useSyntaxServer.auto": "Spawn both a full server and a lighter weight server dedicated to syntax operations. The syntax server is used to speed up syntax operations and provide IntelliSense while projects are loading.", - "configuration.tsserver.maxTsServerMemory": "The maximum amount of memory (in MB) to allocate to the TypeScript server process.", + "configuration.tsserver.maxTsServerMemory": "The maximum amount of memory (in MB) to allocate to the TypeScript server process. To use a memory limit greater than 4 GB, use `#typescript.tsserver.nodePath#` to run TS Server with a custom Node installation.", "configuration.tsserver.experimental.enableProjectDiagnostics": "(Experimental) Enables project wide error reporting.", "typescript.locale": "Sets the locale used to report JavaScript and TypeScript errors. Defaults to use VS Code's locale.", "configuration.implicitProjectConfig.module": "Sets the module system for the program. See more: https://www.typescriptlang.org/tsconfig#module.", @@ -213,6 +213,7 @@ "configuration.suggest.objectLiteralMethodSnippets.enabled": "Enable/disable snippet completions for methods in object literals. Requires using TypeScript 4.7+ in the workspace.", "configuration.tsserver.web.projectWideIntellisense.enabled": "Enable/disable project-wide IntelliSense on web. Requires that VS Code is running in a trusted context.", "configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors": "Suppresses semantic errors. This is needed when using external packages as these can't be included analyzed on web.", + "configuration.tsserver.nodePath": "Run TS Server on a custom Node installation. This can be a path to a Node executable, or 'node' if you want VS Code to detect a Node installation.", "configuration.experimental.tsserver.web.typeAcquisition.enabled": "Enable/disable package acquisition on the web.", "walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js", "walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.", diff --git a/extensions/typescript-language-features/src/configuration/configuration.browser.ts b/extensions/typescript-language-features/src/configuration/configuration.browser.ts index cfe7ed8b74d..15d4705de0e 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.browser.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.browser.ts @@ -16,4 +16,13 @@ export class BrowserServiceConfigurationProvider extends BaseServiceConfiguratio protected readLocalTsdk(_configuration: vscode.WorkspaceConfiguration): string | null { return null; } + + // On browsers, we don't run TSServer on Node + protected readLocalNodePath(_configuration: vscode.WorkspaceConfiguration): string | null { + return null; + } + + protected override readGlobalNodePath(_configuration: vscode.WorkspaceConfiguration): string | null { + return null; + } } diff --git a/extensions/typescript-language-features/src/configuration/configuration.electron.ts b/extensions/typescript-language-features/src/configuration/configuration.electron.ts index db84603c314..0c2a7ab12f7 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.electron.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.electron.ts @@ -6,7 +6,10 @@ import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; +import * as child_process from 'child_process'; +import * as fs from 'fs'; import { BaseServiceConfigurationProvider } from './configuration'; +import { RelativeWorkspacePathResolver } from '../utils/relativePathResolver'; export class ElectronServiceConfigurationProvider extends BaseServiceConfigurationProvider { @@ -35,4 +38,65 @@ export class ElectronServiceConfigurationProvider extends BaseServiceConfigurati } return null; } + + protected readLocalNodePath(configuration: vscode.WorkspaceConfiguration): string | null { + return this.validatePath(this.readLocalNodePathWorker(configuration)); + } + + private readLocalNodePathWorker(configuration: vscode.WorkspaceConfiguration): string | null { + const inspect = configuration.inspect('typescript.tsserver.nodePath'); + if (inspect?.workspaceValue && typeof inspect.workspaceValue === 'string') { + if (inspect.workspaceValue === 'node') { + return this.findNodePath(); + } + const fixedPath = this.fixPathPrefixes(inspect.workspaceValue); + if (!path.isAbsolute(fixedPath)) { + const workspacePath = RelativeWorkspacePathResolver.asAbsoluteWorkspacePath(fixedPath); + return workspacePath || null; + } + return fixedPath; + } + return null; + } + + protected readGlobalNodePath(configuration: vscode.WorkspaceConfiguration): string | null { + return this.validatePath(this.readGlobalNodePathWorker(configuration)); + } + + private readGlobalNodePathWorker(configuration: vscode.WorkspaceConfiguration): string | null { + const inspect = configuration.inspect('typescript.tsserver.nodePath'); + if (inspect?.globalValue && typeof inspect.globalValue === 'string') { + if (inspect.globalValue === 'node') { + return this.findNodePath(); + } + const fixedPath = this.fixPathPrefixes(inspect.globalValue); + if (path.isAbsolute(fixedPath)) { + return fixedPath; + } + } + return null; + } + + private findNodePath(): string | null { + try { + const out = child_process.execFileSync('node', ['-e', 'console.log(process.execPath)'], { + windowsHide: true, + timeout: 2000, + cwd: vscode.workspace.workspaceFolders?.[0].uri.fsPath, + encoding: 'utf-8', + }); + return out.trim(); + } catch (error) { + vscode.window.showWarningMessage(vscode.l10n.t("Could not detect a Node installation to run TS Server.")); + return null; + } + } + + private validatePath(nodePath: string | null): string | null { + if (nodePath && (!fs.existsSync(nodePath) || fs.lstatSync(nodePath).isDirectory())) { + vscode.window.showWarningMessage(vscode.l10n.t("The path {0} doesn\'t point to a valid Node installation to run TS Server. Falling back to bundled Node.", nodePath)); + return null; + } + return nodePath; + } } diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index f3a5817146b..0d60cd74932 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -120,6 +120,8 @@ export interface TypeScriptServiceConfiguration { readonly watchOptions: Proto.WatchOptions | undefined; readonly includePackageJsonAutoImports: 'auto' | 'on' | 'off' | undefined; readonly enableTsServerTracing: boolean; + readonly localNodePath: string | null; + readonly globalNodePath: string | null; } export function areServiceConfigurationsEqual(a: TypeScriptServiceConfiguration, b: TypeScriptServiceConfiguration): boolean { @@ -154,11 +156,15 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu watchOptions: this.readWatchOptions(configuration), includePackageJsonAutoImports: this.readIncludePackageJsonAutoImports(configuration), enableTsServerTracing: this.readEnableTsServerTracing(configuration), + localNodePath: this.readLocalNodePath(configuration), + globalNodePath: this.readGlobalNodePath(configuration), }; } protected abstract readGlobalTsdk(configuration: vscode.WorkspaceConfiguration): string | null; protected abstract readLocalTsdk(configuration: vscode.WorkspaceConfiguration): string | null; + protected abstract readLocalNodePath(configuration: vscode.WorkspaceConfiguration): string | null; + protected abstract readGlobalNodePath(configuration: vscode.WorkspaceConfiguration): string | null; protected readTsServerLogLevel(configuration: vscode.WorkspaceConfiguration): TsServerLogLevel { const setting = configuration.get('typescript.tsserver.log', 'off'); diff --git a/extensions/typescript-language-features/src/tsServer/nodeManager.ts b/extensions/typescript-language-features/src/tsServer/nodeManager.ts new file mode 100644 index 00000000000..037fc1898e8 --- /dev/null +++ b/extensions/typescript-language-features/src/tsServer/nodeManager.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { TypeScriptServiceConfiguration } from '../configuration/configuration'; +import { setImmediate } from '../utils/async'; +import { Disposable } from '../utils/dispose'; + + +const useWorkspaceNodeStorageKey = 'typescript.useWorkspaceNode'; +const lastKnownWorkspaceNodeStorageKey = 'typescript.lastKnownWorkspaceNode'; +type UseWorkspaceNodeState = undefined | boolean; +type LastKnownWorkspaceNodeState = undefined | string; + +export class NodeVersionManager extends Disposable { + private _currentVersion: string | undefined; + + public constructor( + private configuration: TypeScriptServiceConfiguration, + private readonly workspaceState: vscode.Memento + ) { + super(); + + this._currentVersion = this.configuration.globalNodePath || undefined; + if (vscode.workspace.isTrusted) { + const workspaceVersion = this.configuration.localNodePath; + if (workspaceVersion) { + const useWorkspaceNode = this.canUseWorkspaceNode(workspaceVersion); + if (useWorkspaceNode === undefined) { + setImmediate(() => { + this.promptAndSetWorkspaceNode(); + }); + } + else if (useWorkspaceNode) { + this._currentVersion = workspaceVersion; + } + } + } + else { + this._disposables.push(vscode.workspace.onDidGrantWorkspaceTrust(() => { + const workspaceVersion = this.configuration.localNodePath; + if (workspaceVersion) { + const useWorkspaceNode = this.canUseWorkspaceNode(workspaceVersion); + if (useWorkspaceNode === undefined) { + setImmediate(() => { + this.promptAndSetWorkspaceNode(); + }); + } + else if (useWorkspaceNode) { + this.updateActiveVersion(workspaceVersion); + } + } + })); + } + } + + private readonly _onDidPickNewVersion = this._register(new vscode.EventEmitter()); + public readonly onDidPickNewVersion = this._onDidPickNewVersion.event; + + public get currentVersion(): string | undefined { + return this._currentVersion; + } + + public async updateConfiguration(nextConfiguration: TypeScriptServiceConfiguration) { + const oldConfiguration = this.configuration; + this.configuration = nextConfiguration; + if (oldConfiguration.globalNodePath !== nextConfiguration.globalNodePath + || oldConfiguration.localNodePath !== nextConfiguration.localNodePath) { + await this.computeNewVersion(); + } + } + + private async computeNewVersion() { + let version = this.configuration.globalNodePath || undefined; + const workspaceVersion = this.configuration.localNodePath; + if (vscode.workspace.isTrusted && workspaceVersion) { + const useWorkspaceNode = this.canUseWorkspaceNode(workspaceVersion); + if (useWorkspaceNode === undefined) { + version = await this.promptUseWorkspaceNode() || version; + } + else if (useWorkspaceNode) { + version = workspaceVersion; + } + } + this.updateActiveVersion(version); + } + + private async promptUseWorkspaceNode(): Promise { + const workspaceVersion = this.configuration.localNodePath; + if (workspaceVersion === null) { + throw new Error('Could not prompt to use workspace Node installation because no workspace Node installation is specified'); + } + + const allow = vscode.l10n.t("Yes"); + const disallow = vscode.l10n.t("No"); + const dismiss = vscode.l10n.t("Not now"); + + const result = await vscode.window.showInformationMessage(vscode.l10n.t("This workspace wants to use the Node installation at '{0}' to run TS Server. Would you like to use it?", workspaceVersion), + allow, + disallow, + dismiss, + ); + + let version = undefined; + switch (result) { + case allow: + await this.setUseWorkspaceNodeState(true, workspaceVersion); + version = workspaceVersion; + break; + case disallow: + await this.setUseWorkspaceNodeState(false, workspaceVersion); + break; + case dismiss: + await this.setUseWorkspaceNodeState(undefined, workspaceVersion); + break; + } + return version; + } + + private async promptAndSetWorkspaceNode(): Promise { + const version = await this.promptUseWorkspaceNode(); + if (version !== undefined) { + this.updateActiveVersion(version); + } + } + + private updateActiveVersion(pickedVersion: string | undefined): void { + const oldVersion = this.currentVersion; + this._currentVersion = pickedVersion; + if (oldVersion !== pickedVersion) { + this._onDidPickNewVersion.fire(); + } + } + + private canUseWorkspaceNode(nodeVersion: string): boolean | undefined { + const lastKnownWorkspaceNode = this.workspaceState.get(lastKnownWorkspaceNodeStorageKey); + if (lastKnownWorkspaceNode === nodeVersion) { + return this.workspaceState.get(useWorkspaceNodeStorageKey); + } + return undefined; + } + + private async setUseWorkspaceNodeState(allow: boolean | undefined, nodeVersion: string) { + await this.workspaceState.update(lastKnownWorkspaceNodeStorageKey, nodeVersion); + await this.workspaceState.update(useWorkspaceNodeStorageKey, allow); + } +} diff --git a/extensions/typescript-language-features/src/tsServer/server.ts b/extensions/typescript-language-features/src/tsServer/server.ts index 421c5f5d8e9..883aa6830bd 100644 --- a/extensions/typescript-language-features/src/tsServer/server.ts +++ b/extensions/typescript-language-features/src/tsServer/server.ts @@ -19,6 +19,7 @@ import type * as Proto from './protocol/protocol'; import { EventName } from './protocol/protocol.const'; import { TypeScriptVersionManager } from './versionManager'; import { TypeScriptVersion } from './versionProvider'; +import { NodeVersionManager } from './nodeManager'; export enum ExecutionTarget { Semantic, @@ -70,6 +71,7 @@ export interface TsServerProcessFactory { kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration, versionManager: TypeScriptVersionManager, + nodeVersionManager: NodeVersionManager, tsServerLog: TsServerLog | undefined, ): TsServerProcess; } diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts index c57e6d352c9..bb57c2644b4 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.browser.ts @@ -13,6 +13,7 @@ import type * as Proto from './protocol/protocol'; import { TsServerLog, TsServerProcess, TsServerProcessFactory, TsServerProcessKind } from './server'; import { TypeScriptVersionManager } from './versionManager'; import { TypeScriptVersion } from './versionProvider'; +import { NodeVersionManager } from './nodeManager'; type BrowserWatchEvent = { type: 'watchDirectory' | 'watchFile'; @@ -40,6 +41,7 @@ export class WorkerServerProcessFactory implements TsServerProcessFactory { kind: TsServerProcessKind, _configuration: TypeScriptServiceConfiguration, _versionManager: TypeScriptVersionManager, + _nodeVersionManager: NodeVersionManager, tsServerLog: TsServerLog | undefined, ) { const tsServerPath = version.tsServerPath; diff --git a/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts index b5848d5eb9f..8b0ec2fb7b7 100644 --- a/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts +++ b/extensions/typescript-language-features/src/tsServer/serverProcess.electron.ts @@ -15,6 +15,7 @@ import type * as Proto from './protocol/protocol'; import { TsServerLog, TsServerProcess, TsServerProcessFactory, TsServerProcessKind } from './server'; import { TypeScriptVersionManager } from './versionManager'; import { TypeScriptVersion } from './versionProvider'; +import { NodeVersionManager } from './nodeManager'; const defaultSize: number = 8192; @@ -134,10 +135,12 @@ class Reader extends Disposable { } } -function generatePatchedEnv(env: any, modulePath: string): any { +function generatePatchedEnv(env: any, modulePath: string, hasExecPath: boolean): any { const newEnv = Object.assign({}, env); - newEnv['ELECTRON_RUN_AS_NODE'] = '1'; + if (!hasExecPath) { + newEnv['ELECTRON_RUN_AS_NODE'] = '1'; + } newEnv['NODE_PATH'] = path.join(modulePath, '..', '..', '..'); // Ensure we always have a PATH set @@ -253,6 +256,7 @@ export class ElectronServiceProcessFactory implements TsServerProcessFactory { kind: TsServerProcessKind, configuration: TypeScriptServiceConfiguration, versionManager: TypeScriptVersionManager, + nodeVersionManager: NodeVersionManager, _tsserverLog: TsServerLog | undefined, ): TsServerProcess { let tsServerPath = version.tsServerPath; @@ -263,20 +267,30 @@ export class ElectronServiceProcessFactory implements TsServerProcessFactory { tsServerPath = versionManager.currentVersion.tsServerPath; } - const useIpc = version.apiVersion?.gte(API.v460); + const execPath = nodeVersionManager.currentVersion; + const env = generatePatchedEnv(process.env, tsServerPath, !!execPath); const runtimeArgs = [...args]; + const execArgv = getExecArgv(kind, configuration); + const useIpc = !execPath && version.apiVersion?.gte(API.v460); if (useIpc) { runtimeArgs.push('--useNodeIpc'); } - const childProcess = child_process.fork(tsServerPath, runtimeArgs, { - silent: true, - cwd: undefined, - env: generatePatchedEnv(process.env, tsServerPath), - execArgv: getExecArgv(kind, configuration), - stdio: useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : undefined, - }); + const childProcess = execPath ? + child_process.spawn(execPath, [...execArgv, tsServerPath, ...runtimeArgs], { + shell: true, + windowsHide: true, + cwd: undefined, + env, + }) : + child_process.fork(tsServerPath, runtimeArgs, { + silent: true, + cwd: undefined, + env, + execArgv, + stdio: useIpc ? ['pipe', 'pipe', 'pipe', 'ipc'] : undefined, + }); return useIpc ? new IpcChildServerProcess(childProcess) : new StdioChildServerProcess(childProcess); } diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index 0fa9bedf4a6..52dcf5baa19 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -19,6 +19,7 @@ import { PluginManager } from './plugins'; import { GetErrRoutingTsServer, ITypeScriptServer, SingleTsServer, SyntaxRoutingTsServer, TsServerDelegate, TsServerLog, TsServerProcessFactory, TsServerProcessKind } from './server'; import { TypeScriptVersionManager } from './versionManager'; import { ITypeScriptVersionProvider, TypeScriptVersion } from './versionProvider'; +import { NodeVersionManager } from './nodeManager'; const enum CompositeServerType { /** Run a single server that handles all commands */ @@ -44,6 +45,7 @@ export class TypeScriptServerSpawner { public constructor( private readonly _versionProvider: ITypeScriptVersionProvider, private readonly _versionManager: TypeScriptVersionManager, + private readonly _nodeVersionManager: NodeVersionManager, private readonly _logDirectoryProvider: ILogDirectoryProvider, private readonly _pluginPathsProvider: TypeScriptPluginPathsProvider, private readonly _logger: Logger, @@ -160,7 +162,7 @@ export class TypeScriptServerSpawner { } this._logger.info(`<${kind}> Forking...`); - const process = this._factory.fork(version, args, kind, configuration, this._versionManager, tsServerLog); + const process = this._factory.fork(version, args, kind, configuration, this._versionManager, this._nodeVersionManager, tsServerLog); this._logger.info(`<${kind}> Starting...`); return new SingleTsServer( diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index e00bed60b3f..5b7591bfd8f 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -30,6 +30,7 @@ import { TelemetryProperties, TelemetryReporter, VSCodeTelemetryReporter } from import Tracer from './logging/tracer'; import { ProjectType, inferredProjectCompilerOptions } from './tsconfig'; import { Schemes } from './configuration/schemes'; +import { NodeVersionManager } from './tsServer/nodeManager'; export interface TsDiagnostics { @@ -103,6 +104,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType private _configuration: TypeScriptServiceConfiguration; private readonly pluginPathsProvider: TypeScriptPluginPathsProvider; private readonly _versionManager: TypeScriptVersionManager; + private readonly _nodeVersionManager: NodeVersionManager; private readonly logger: Logger; private readonly tracer: Tracer; @@ -173,6 +175,11 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.restartTsServer(); })); + this._nodeVersionManager = this._register(new NodeVersionManager(this._configuration, context.workspaceState)); + this._register(this._nodeVersionManager.onDidPickNewVersion(() => { + this.restartTsServer(); + })); + this.bufferSyncSupport = new BufferSyncSupport(this, allModeIds, onCaseInsenitiveFileSystem); this.onReady(() => { this.bufferSyncSupport.listen(); }); @@ -192,6 +199,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.versionProvider.updateConfiguration(this._configuration); this._versionManager.updateConfiguration(this._configuration); this.pluginPathsProvider.updateConfiguration(this._configuration); + this._nodeVersionManager.updateConfiguration(this._configuration); if (this.serverState.type === ServerState.Type.Running) { if (!this._configuration.implicitProjectConfiguration.isEqualTo(oldConfiguration.implicitProjectConfiguration)) { @@ -212,8 +220,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType } return this.apiVersion.fullVersionString; }); + this.diagnosticsManager = new DiagnosticsManager('typescript', this._configuration, this.telemetryReporter, onCaseInsenitiveFileSystem); - this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory); + this.typescriptServerSpawner = new TypeScriptServerSpawner(this.versionProvider, this._versionManager, this._nodeVersionManager, this.logDirectoryProvider, this.pluginPathsProvider, this.logger, this.telemetryReporter, this.tracer, this.processFactory); this._register(this.pluginManager.onDidUpdateConfig(update => { this.configurePlugin(update.pluginId, update.config); @@ -387,6 +396,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType } this.info(`Using tsserver from: ${version.path}`); + const nodePath = this._nodeVersionManager.currentVersion; + if (nodePath) { + this.info(`Using Node installation from ${nodePath} to run TS Server`); + } const apiVersion = version.apiVersion || API.defaultVersion; const mytoken = ++this.token; From d3a7a8dfb6af913092c97b55337d4907fc22be3f Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 6 Sep 2023 13:30:25 -0700 Subject: [PATCH 575/607] dispose disposables --- .../api/common/extHostNotebookKernels.ts | 2 - .../browser/extHostNotebookKernel.test.ts | 38 +++++++++++-------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index f3e1263c357..2401b9f34f3 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -111,7 +111,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { const _defaultExecutHandler = () => console.warn(`NO execute handler from notebook controller '${data.id}' of extension: '${extension.identifier}'`); let isDisposed = false; - const commandDisposables = new DisposableStore(); const onDidChangeSelection = new Emitter<{ selected: boolean; notebook: vscode.NotebookDocument }>(); const onDidReceiveMessage = new Emitter<{ editor: vscode.NotebookEditor; message: any }>(); @@ -236,7 +235,6 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { this._logService.trace(`NotebookController[${handle}], DISPOSED`); isDisposed = true; this._kernelData.delete(handle); - commandDisposables.dispose(); onDidChangeSelection.dispose(); onDidReceiveMessage.dispose(); this._proxy.$removeKernel(handle); diff --git a/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts b/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts index 8186540ef63..d9448b421e9 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts +++ b/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts @@ -28,9 +28,9 @@ import { mock } from 'vs/workbench/test/common/workbenchTestServices'; import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; import { ExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('NotebookKernel', function () { - let rpcProtocol: TestRPCProtocol; let extHostNotebookKernels: ExtHostNotebookKernels; let notebook: ExtHostNotebookDocument; @@ -52,6 +52,7 @@ suite('NotebookKernel', function () { teardown(function () { disposables.clear(); }); + ensureNoDisposablesAreLeakedInTestSuite(); setup(async function () { cellExecuteCreate.length = 0; cellExecuteUpdates.length = 0; @@ -91,7 +92,7 @@ suite('NotebookKernel', function () { override async $unregisterNotebookSerializer() { } }); extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); - extHostDocuments = new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors); + extHostDocuments = disposables.add(new ExtHostDocuments(rpcProtocol, extHostDocumentsAndEditors)); extHostCommands = new ExtHostCommands(rpcProtocol, new NullLogService(), new class extends mock() { override onExtensionError(): boolean { return true; @@ -177,7 +178,7 @@ suite('NotebookKernel', function () { test('update kernel', async function () { - const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); + const kernel = disposables.add(extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo')); await rpcProtocol.sync(); assert.ok(kernel); @@ -196,7 +197,7 @@ suite('NotebookKernel', function () { }); test('execute - simple createNotebookCellExecution', function () { - const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); + const kernel = disposables.add(extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo')); extHostNotebookKernels.$acceptNotebookAssociation(0, notebook.uri, true); @@ -207,17 +208,18 @@ suite('NotebookKernel', function () { }); test('createNotebookCellExecution, must be selected/associated', function () { - const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); + const kernel = disposables.add(extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo')); assert.throws(() => { kernel.createNotebookCellExecution(notebook.apiNotebook.cellAt(0)); }); extHostNotebookKernels.$acceptNotebookAssociation(0, notebook.uri, true); - kernel.createNotebookCellExecution(notebook.apiNotebook.cellAt(0)); + const execution = kernel.createNotebookCellExecution(notebook.apiNotebook.cellAt(0)); + execution.end(true); }); test('createNotebookCellExecution, cell must be alive', function () { - const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); + const kernel = disposables.add(extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo')); const cell1 = notebook.apiNotebook.cellAt(0); @@ -242,14 +244,14 @@ suite('NotebookKernel', function () { let interruptCallCount = 0; let tokenCancelCount = 0; - const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); + const kernel = disposables.add(extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo')); kernel.interruptHandler = () => { interruptCallCount += 1; }; extHostNotebookKernels.$acceptNotebookAssociation(0, notebook.uri, true); const cell1 = notebook.apiNotebook.cellAt(0); const task = kernel.createNotebookCellExecution(cell1); - task.token.onCancellationRequested(() => tokenCancelCount += 1); + disposables.add(task.token.onCancellationRequested(() => tokenCancelCount += 1)); await extHostNotebookKernels.$cancelCells(0, notebook.uri, [0]); assert.strictEqual(interruptCallCount, 1); @@ -258,11 +260,14 @@ suite('NotebookKernel', function () { await extHostNotebookKernels.$cancelCells(0, notebook.uri, [0]); assert.strictEqual(interruptCallCount, 2); assert.strictEqual(tokenCancelCount, 0); + + // should cancelling the cells end the execution task? + task.end(false); }); test('set outputs on cancel', async function () { - const kernel = extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo'); + const kernel = disposables.add(extHostNotebookKernels.createNotebookController(nullExtensionDescription, 'foo', '*', 'Foo')); extHostNotebookKernels.$acceptNotebookAssociation(0, notebook.uri, true); const cell1 = notebook.apiNotebook.cellAt(0); @@ -271,11 +276,13 @@ suite('NotebookKernel', function () { const b = new Barrier(); - task.token.onCancellationRequested(async () => { - await task.replaceOutput(new NotebookCellOutput([NotebookCellOutputItem.text('canceled')])); - task.end(true); - b.open(); // use barrier to signal that cancellation has happened - }); + disposables.add( + task.token.onCancellationRequested(async () => { + await task.replaceOutput(new NotebookCellOutput([NotebookCellOutputItem.text('canceled')])); + task.end(true); + b.open(); // use barrier to signal that cancellation has happened + }) + ); cellExecuteUpdates.length = 0; await extHostNotebookKernels.$cancelCells(0, notebook.uri, [0]); @@ -331,3 +338,4 @@ suite('NotebookKernel', function () { assert.ok(found); }); }); + From df703092cd69d660608c3979b9db2e00ddb6185e Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 6 Sep 2023 13:30:58 -0700 Subject: [PATCH 576/607] Fix #192311 (#192344) --- .../browser/services/notebookExecutionStateServiceImpl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts index 9ebea229e18..a5736966b8a 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts @@ -84,7 +84,7 @@ export class NotebookExecutionStateService extends Disposable implements INotebo getCellExecutionsByHandleForNotebook(notebook: URI): Map | undefined { const exeMap = this._executions.get(notebook); - return exeMap ?? undefined; + return exeMap ? new Map(exeMap.entries()) : undefined; } private _onCellExecutionDidChange(notebookUri: URI, cellHandle: number, exe: CellExecution): void { From 3500878275b4d778c85fd0315f305202666272d2 Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 6 Sep 2023 14:17:00 -0700 Subject: [PATCH 577/607] another test suite --- src/vs/workbench/api/test/browser/extHostNotebook.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts index d13779aaede..569675861e7 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts +++ b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts @@ -26,6 +26,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { IExtHostTelemetry } from 'vs/workbench/api/common/extHostTelemetry'; import { ExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer'; import { ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('NotebookCell#Document', function () { @@ -45,6 +46,8 @@ suite('NotebookCell#Document', function () { disposables.clear(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + setup(async function () { rpcProtocol = new TestRPCProtocol(); rpcProtocol.set(MainContext.MainThreadCommands, new class extends mock() { From a9b5269b842105d21c6b63484d643ee065af7885 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 6 Sep 2023 14:20:12 -0700 Subject: [PATCH 578/607] testing: fix icon order mismatch in test explorer and output (#192343) Fixes #191182 --- .../contrib/testing/browser/testingOutputPeek.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 79454864a6b..0b42f25d628 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -2245,14 +2245,6 @@ class TreeActionsProvider { if (element instanceof TestCaseElement) { const extId = element.test.item.extId; - primary.push(new Action( - 'testing.outputPeek.goToFile', - localize('testing.goToFile', "Go to Source"), - ThemeIcon.asClassName(Codicon.goToFile), - undefined, - () => this.commandService.executeCommand('vscode.revealTest', extId), - )); - if (element.test.tasks[element.taskIndex].messages.some(m => m.type === TestMessageType.Output)) { primary.push(new Action( 'testing.outputPeek.showResultOutput', @@ -2290,6 +2282,14 @@ class TreeActionsProvider { () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Debug, extId), )); } + + primary.push(new Action( + 'testing.outputPeek.goToFile', + localize('testing.goToFile', "Go to Source"), + ThemeIcon.asClassName(Codicon.goToFile), + undefined, + () => this.commandService.executeCommand('vscode.revealTest', extId), + )); } if (element instanceof TestMessageElement) { From 4548d12f497f52f5708d00c73edc7a3d356d17ad Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 6 Sep 2023 14:20:56 -0700 Subject: [PATCH 579/607] api: fix docs for extensionmode (#192350) --- src/vscode-dts/vscode.d.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index 97f52cb344d..17ee7553c42 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -7764,9 +7764,8 @@ declare module 'vscode' { readonly logPath: string; /** - * The mode the extension is running in. This is specific to the current - * extension. One extension may be in `ExtensionMode.Development` while - * other extensions in the host run in `ExtensionMode.Release`. + * The mode the extension is running in. See {@link ExtensionMode} + * for possible values and scenarios. */ readonly extensionMode: ExtensionMode; From 173bc6d97c834b0b999fbe637c11fbc1c9c920be Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Wed, 6 Sep 2023 23:31:25 +0200 Subject: [PATCH 580/607] Option for auto closing comments (#192335) Implement support for `autoClosingComments` --------- Co-authored-by: Sergey Sharybin --- src/vs/editor/common/config/editorOptions.ts | 20 ++ .../common/cursor/cursorTypeOperations.ts | 31 +- src/vs/editor/common/cursorCommon.ts | 12 +- .../common/standalone/standaloneEnums.ts | 281 ++++++++--------- .../test/browser/controller/cursor.test.ts | 21 ++ src/vs/monaco.d.ts | 287 +++++++++--------- 6 files changed, 362 insertions(+), 290 deletions(-) diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 31fd45a4430..f138511c9a4 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -459,6 +459,11 @@ export interface IEditorOptions { * Defaults to language defined behavior. */ autoClosingBrackets?: EditorAutoClosingStrategy; + /** + * Options for auto closing comments. + * Defaults to language defined behavior. + */ + autoClosingComments?: EditorAutoClosingStrategy; /** * Options for auto closing quotes. * Defaults to language defined behavior. @@ -5011,6 +5016,7 @@ export const enum EditorOption { ariaLabel, ariaRequired, autoClosingBrackets, + autoClosingComments, screenReaderAnnounceInlineSuggestion, autoClosingDelete, autoClosingOvertype, @@ -5205,6 +5211,20 @@ export const EditorOptions = { description: nls.localize('autoClosingBrackets', "Controls whether the editor should automatically close brackets after the user adds an opening bracket.") } )), + autoClosingComments: register(new EditorStringEnumOption( + EditorOption.autoClosingComments, 'autoClosingComments', + 'languageDefined' as 'always' | 'languageDefined' | 'beforeWhitespace' | 'never', + ['always', 'languageDefined', 'beforeWhitespace', 'never'] as const, + { + enumDescriptions: [ + '', + nls.localize('editor.autoClosingComments.languageDefined', "Use language configurations to determine when to autoclose comments."), + nls.localize('editor.autoClosingComments.beforeWhitespace', "Autoclose comments only when the cursor is to the left of whitespace."), + '', + ], + description: nls.localize('autoClosingComments', "Controls whether the editor should automatically close comments after the user adds an opening comment.") + } + )), autoClosingDelete: register(new EditorStringEnumOption( EditorOption.autoClosingDelete, 'autoClosingDelete', 'auto' as 'always' | 'auto' | 'never', diff --git a/src/vs/editor/common/cursor/cursorTypeOperations.ts b/src/vs/editor/common/cursor/cursorTypeOperations.ts index e735c14056f..e71f02a960e 100644 --- a/src/vs/editor/common/cursor/cursorTypeOperations.ts +++ b/src/vs/editor/common/cursor/cursorTypeOperations.ts @@ -19,7 +19,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { EnterAction, IndentAction, StandardAutoClosingPairConditional } from 'vs/editor/common/languages/languageConfiguration'; import { getIndentationAtPosition } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { IElectricAction } from 'vs/editor/common/languages/supports/electricCharacter'; -import { EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; +import { EditorAutoClosingStrategy, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; import { createScopedLineTokens } from 'vs/editor/common/languages/supports'; import { getIndentActionForType, getIndentForEnter, getInheritIndentForLine } from 'vs/editor/common/languages/autoIndent'; import { getEnterAction } from 'vs/editor/common/languages/enterAction'; @@ -565,13 +565,6 @@ export class TypeOperations { } private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, chIsAlreadyTyped: boolean): string | null { - const chIsQuote = isQuote(ch); - const autoCloseConfig = (chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets); - const shouldAutoCloseBefore = (chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket); - - if (autoCloseConfig === 'never') { - return null; - } for (const selection of selections) { if (!selection.isEmpty()) { @@ -603,6 +596,28 @@ export class TypeOperations { return null; } + let autoCloseConfig: EditorAutoClosingStrategy; + let shouldAutoCloseBefore: (ch: string) => boolean; + + const chIsQuote = isQuote(ch); + if (chIsQuote) { + autoCloseConfig = config.autoClosingQuotes; + shouldAutoCloseBefore = config.shouldAutoCloseBefore.quote; + } else { + const pairIsForComments = config.blockCommentStartToken ? pair.open.includes(config.blockCommentStartToken) : false; + if (pairIsForComments) { + autoCloseConfig = config.autoClosingComments; + shouldAutoCloseBefore = config.shouldAutoCloseBefore.comment; + } else { + autoCloseConfig = config.autoClosingBrackets; + shouldAutoCloseBefore = config.shouldAutoCloseBefore.bracket; + } + } + + if (autoCloseConfig === 'never') { + return null; + } + // Sometimes, it is possible to have two auto-closing pairs that have a containment relationship // e.g. when having [(,)] and [(*,*)] // - when typing (, the resulting state is (|) diff --git a/src/vs/editor/common/cursorCommon.ts b/src/vs/editor/common/cursorCommon.ts index 9507d83056b..13b95ad1299 100644 --- a/src/vs/editor/common/cursorCommon.ts +++ b/src/vs/editor/common/cursorCommon.ts @@ -66,6 +66,7 @@ export class CursorConfiguration { public readonly multiCursorPaste: 'spread' | 'full'; public readonly multiCursorLimit: number; public readonly autoClosingBrackets: EditorAutoClosingStrategy; + public readonly autoClosingComments: EditorAutoClosingStrategy; public readonly autoClosingQuotes: EditorAutoClosingStrategy; public readonly autoClosingDelete: EditorAutoClosingEditStrategy; public readonly autoClosingOvertype: EditorAutoClosingEditStrategy; @@ -73,7 +74,8 @@ export class CursorConfiguration { public readonly autoIndent: EditorAutoIndentStrategy; public readonly autoClosingPairs: AutoClosingPairs; public readonly surroundingPairs: CharacterMap; - public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean; bracket: (ch: string) => boolean }; + public readonly blockCommentStartToken: string | null; + public readonly shouldAutoCloseBefore: { quote: (ch: string) => boolean; bracket: (ch: string) => boolean; comment: (ch: string) => boolean }; private readonly _languageId: string; private _electricChars: { [key: string]: boolean } | null; @@ -87,6 +89,7 @@ export class CursorConfiguration { || e.hasChanged(EditorOption.multiCursorPaste) || e.hasChanged(EditorOption.multiCursorLimit) || e.hasChanged(EditorOption.autoClosingBrackets) + || e.hasChanged(EditorOption.autoClosingComments) || e.hasChanged(EditorOption.autoClosingQuotes) || e.hasChanged(EditorOption.autoClosingDelete) || e.hasChanged(EditorOption.autoClosingOvertype) @@ -125,6 +128,7 @@ export class CursorConfiguration { this.multiCursorPaste = options.get(EditorOption.multiCursorPaste); this.multiCursorLimit = options.get(EditorOption.multiCursorLimit); this.autoClosingBrackets = options.get(EditorOption.autoClosingBrackets); + this.autoClosingComments = options.get(EditorOption.autoClosingComments); this.autoClosingQuotes = options.get(EditorOption.autoClosingQuotes); this.autoClosingDelete = options.get(EditorOption.autoClosingDelete); this.autoClosingOvertype = options.get(EditorOption.autoClosingOvertype); @@ -136,7 +140,8 @@ export class CursorConfiguration { this.shouldAutoCloseBefore = { quote: this._getShouldAutoClose(languageId, this.autoClosingQuotes, true), - bracket: this._getShouldAutoClose(languageId, this.autoClosingBrackets, false) + comment: this._getShouldAutoClose(languageId, this.autoClosingComments, false), + bracket: this._getShouldAutoClose(languageId, this.autoClosingBrackets, false), }; this.autoClosingPairs = this.languageConfigurationService.getLanguageConfiguration(languageId).getAutoClosingPairs(); @@ -147,6 +152,9 @@ export class CursorConfiguration { this.surroundingPairs[pair.open] = pair.close; } } + + const commentsConfiguration = this.languageConfigurationService.getLanguageConfiguration(languageId).comments; + this.blockCommentStartToken = commentsConfiguration?.blockCommentStartToken ?? null; } public get electricChars() { diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 7cc3add3df1..ec6cf691492 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -180,146 +180,147 @@ export enum EditorOption { ariaLabel = 4, ariaRequired = 5, autoClosingBrackets = 6, - screenReaderAnnounceInlineSuggestion = 7, - autoClosingDelete = 8, - autoClosingOvertype = 9, - autoClosingQuotes = 10, - autoIndent = 11, - automaticLayout = 12, - autoSurround = 13, - bracketPairColorization = 14, - guides = 15, - codeLens = 16, - codeLensFontFamily = 17, - codeLensFontSize = 18, - colorDecorators = 19, - colorDecoratorsLimit = 20, - columnSelection = 21, - comments = 22, - contextmenu = 23, - copyWithSyntaxHighlighting = 24, - cursorBlinking = 25, - cursorSmoothCaretAnimation = 26, - cursorStyle = 27, - cursorSurroundingLines = 28, - cursorSurroundingLinesStyle = 29, - cursorWidth = 30, - disableLayerHinting = 31, - disableMonospaceOptimizations = 32, - domReadOnly = 33, - dragAndDrop = 34, - dropIntoEditor = 35, - emptySelectionClipboard = 36, - experimentalWhitespaceRendering = 37, - extraEditorClassName = 38, - fastScrollSensitivity = 39, - find = 40, - fixedOverflowWidgets = 41, - folding = 42, - foldingStrategy = 43, - foldingHighlight = 44, - foldingImportsByDefault = 45, - foldingMaximumRegions = 46, - unfoldOnClickAfterEndOfLine = 47, - fontFamily = 48, - fontInfo = 49, - fontLigatures = 50, - fontSize = 51, - fontWeight = 52, - fontVariations = 53, - formatOnPaste = 54, - formatOnType = 55, - glyphMargin = 56, - gotoLocation = 57, - hideCursorInOverviewRuler = 58, - hover = 59, - inDiffEditor = 60, - inlineSuggest = 61, - letterSpacing = 62, - lightbulb = 63, - lineDecorationsWidth = 64, - lineHeight = 65, - lineNumbers = 66, - lineNumbersMinChars = 67, - linkedEditing = 68, - links = 69, - matchBrackets = 70, - minimap = 71, - mouseStyle = 72, - mouseWheelScrollSensitivity = 73, - mouseWheelZoom = 74, - multiCursorMergeOverlapping = 75, - multiCursorModifier = 76, - multiCursorPaste = 77, - multiCursorLimit = 78, - occurrencesHighlight = 79, - overviewRulerBorder = 80, - overviewRulerLanes = 81, - padding = 82, - pasteAs = 83, - parameterHints = 84, - peekWidgetDefaultFocus = 85, - definitionLinkOpensInPeek = 86, - quickSuggestions = 87, - quickSuggestionsDelay = 88, - readOnly = 89, - readOnlyMessage = 90, - renameOnType = 91, - renderControlCharacters = 92, - renderFinalNewline = 93, - renderLineHighlight = 94, - renderLineHighlightOnlyWhenFocus = 95, - renderValidationDecorations = 96, - renderWhitespace = 97, - revealHorizontalRightPadding = 98, - roundedSelection = 99, - rulers = 100, - scrollbar = 101, - scrollBeyondLastColumn = 102, - scrollBeyondLastLine = 103, - scrollPredominantAxis = 104, - selectionClipboard = 105, - selectionHighlight = 106, - selectOnLineNumbers = 107, - showFoldingControls = 108, - showUnused = 109, - snippetSuggestions = 110, - smartSelect = 111, - smoothScrolling = 112, - stickyScroll = 113, - stickyTabStops = 114, - stopRenderingLineAfter = 115, - suggest = 116, - suggestFontSize = 117, - suggestLineHeight = 118, - suggestOnTriggerCharacters = 119, - suggestSelection = 120, - tabCompletion = 121, - tabIndex = 122, - unicodeHighlighting = 123, - unusualLineTerminators = 124, - useShadowDOM = 125, - useTabStops = 126, - wordBreak = 127, - wordSeparators = 128, - wordWrap = 129, - wordWrapBreakAfterCharacters = 130, - wordWrapBreakBeforeCharacters = 131, - wordWrapColumn = 132, - wordWrapOverride1 = 133, - wordWrapOverride2 = 134, - wrappingIndent = 135, - wrappingStrategy = 136, - showDeprecated = 137, - inlayHints = 138, - editorClassName = 139, - pixelRatio = 140, - tabFocusMode = 141, - layoutInfo = 142, - wrappingInfo = 143, - defaultColorDecorators = 144, - colorDecoratorsActivatedOn = 145, - inlineCompletionsAccessibilityVerbose = 146 + autoClosingComments = 7, + screenReaderAnnounceInlineSuggestion = 8, + autoClosingDelete = 9, + autoClosingOvertype = 10, + autoClosingQuotes = 11, + autoIndent = 12, + automaticLayout = 13, + autoSurround = 14, + bracketPairColorization = 15, + guides = 16, + codeLens = 17, + codeLensFontFamily = 18, + codeLensFontSize = 19, + colorDecorators = 20, + colorDecoratorsLimit = 21, + columnSelection = 22, + comments = 23, + contextmenu = 24, + copyWithSyntaxHighlighting = 25, + cursorBlinking = 26, + cursorSmoothCaretAnimation = 27, + cursorStyle = 28, + cursorSurroundingLines = 29, + cursorSurroundingLinesStyle = 30, + cursorWidth = 31, + disableLayerHinting = 32, + disableMonospaceOptimizations = 33, + domReadOnly = 34, + dragAndDrop = 35, + dropIntoEditor = 36, + emptySelectionClipboard = 37, + experimentalWhitespaceRendering = 38, + extraEditorClassName = 39, + fastScrollSensitivity = 40, + find = 41, + fixedOverflowWidgets = 42, + folding = 43, + foldingStrategy = 44, + foldingHighlight = 45, + foldingImportsByDefault = 46, + foldingMaximumRegions = 47, + unfoldOnClickAfterEndOfLine = 48, + fontFamily = 49, + fontInfo = 50, + fontLigatures = 51, + fontSize = 52, + fontWeight = 53, + fontVariations = 54, + formatOnPaste = 55, + formatOnType = 56, + glyphMargin = 57, + gotoLocation = 58, + hideCursorInOverviewRuler = 59, + hover = 60, + inDiffEditor = 61, + inlineSuggest = 62, + letterSpacing = 63, + lightbulb = 64, + lineDecorationsWidth = 65, + lineHeight = 66, + lineNumbers = 67, + lineNumbersMinChars = 68, + linkedEditing = 69, + links = 70, + matchBrackets = 71, + minimap = 72, + mouseStyle = 73, + mouseWheelScrollSensitivity = 74, + mouseWheelZoom = 75, + multiCursorMergeOverlapping = 76, + multiCursorModifier = 77, + multiCursorPaste = 78, + multiCursorLimit = 79, + occurrencesHighlight = 80, + overviewRulerBorder = 81, + overviewRulerLanes = 82, + padding = 83, + pasteAs = 84, + parameterHints = 85, + peekWidgetDefaultFocus = 86, + definitionLinkOpensInPeek = 87, + quickSuggestions = 88, + quickSuggestionsDelay = 89, + readOnly = 90, + readOnlyMessage = 91, + renameOnType = 92, + renderControlCharacters = 93, + renderFinalNewline = 94, + renderLineHighlight = 95, + renderLineHighlightOnlyWhenFocus = 96, + renderValidationDecorations = 97, + renderWhitespace = 98, + revealHorizontalRightPadding = 99, + roundedSelection = 100, + rulers = 101, + scrollbar = 102, + scrollBeyondLastColumn = 103, + scrollBeyondLastLine = 104, + scrollPredominantAxis = 105, + selectionClipboard = 106, + selectionHighlight = 107, + selectOnLineNumbers = 108, + showFoldingControls = 109, + showUnused = 110, + snippetSuggestions = 111, + smartSelect = 112, + smoothScrolling = 113, + stickyScroll = 114, + stickyTabStops = 115, + stopRenderingLineAfter = 116, + suggest = 117, + suggestFontSize = 118, + suggestLineHeight = 119, + suggestOnTriggerCharacters = 120, + suggestSelection = 121, + tabCompletion = 122, + tabIndex = 123, + unicodeHighlighting = 124, + unusualLineTerminators = 125, + useShadowDOM = 126, + useTabStops = 127, + wordBreak = 128, + wordSeparators = 129, + wordWrap = 130, + wordWrapBreakAfterCharacters = 131, + wordWrapBreakBeforeCharacters = 132, + wordWrapColumn = 133, + wordWrapOverride1 = 134, + wordWrapOverride2 = 135, + wrappingIndent = 136, + wrappingStrategy = 137, + showDeprecated = 138, + inlayHints = 139, + editorClassName = 140, + pixelRatio = 141, + tabFocusMode = 142, + layoutInfo = 143, + wrappingInfo = 144, + defaultColorDecorators = 145, + colorDecoratorsActivatedOn = 146, + inlineCompletionsAccessibilityVerbose = 147 } /** diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 5254ef1add9..a2ec4986989 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -1429,6 +1429,9 @@ suite('Editor Controller', () => { function setupAutoClosingLanguage() { disposables.add(languageService.registerLanguage({ id: autoClosingLanguageId })); disposables.add(languageConfigurationService.register(autoClosingLanguageId, { + comments: { + blockComment: ['/*', '*/'] + }, autoClosingPairs: [ { open: '{', close: '}' }, { open: '[', close: ']' }, @@ -5343,6 +5346,24 @@ suite('Editor Controller', () => { }); }); + test('autoClosingPairs - doc comments can be turned off', () => { + usingCursor({ + text: [ + '', + ], + languageId: autoClosingLanguageId, + editorOpts: { + autoClosingComments: 'never' + } + }, (editor, model, viewModel) => { + + model.setValue('/*'); + viewModel.setSelections('test', [new Selection(1, 3, 1, 3)]); + viewModel.type('*', 'keyboard'); + assert.strictEqual(model.getLineContent(1), '/**'); + }); + }); + test('issue #72177: multi-character autoclose with conflicting patterns', () => { const languageId = 'autoClosingModeMultiChar'; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 279a605ba52..6332e76a0b9 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -3428,6 +3428,11 @@ declare namespace monaco.editor { * Defaults to language defined behavior. */ autoClosingBrackets?: EditorAutoClosingStrategy; + /** + * Options for auto closing comments. + * Defaults to language defined behavior. + */ + autoClosingComments?: EditorAutoClosingStrategy; /** * Options for auto closing quotes. * Defaults to language defined behavior. @@ -4697,146 +4702,147 @@ declare namespace monaco.editor { ariaLabel = 4, ariaRequired = 5, autoClosingBrackets = 6, - screenReaderAnnounceInlineSuggestion = 7, - autoClosingDelete = 8, - autoClosingOvertype = 9, - autoClosingQuotes = 10, - autoIndent = 11, - automaticLayout = 12, - autoSurround = 13, - bracketPairColorization = 14, - guides = 15, - codeLens = 16, - codeLensFontFamily = 17, - codeLensFontSize = 18, - colorDecorators = 19, - colorDecoratorsLimit = 20, - columnSelection = 21, - comments = 22, - contextmenu = 23, - copyWithSyntaxHighlighting = 24, - cursorBlinking = 25, - cursorSmoothCaretAnimation = 26, - cursorStyle = 27, - cursorSurroundingLines = 28, - cursorSurroundingLinesStyle = 29, - cursorWidth = 30, - disableLayerHinting = 31, - disableMonospaceOptimizations = 32, - domReadOnly = 33, - dragAndDrop = 34, - dropIntoEditor = 35, - emptySelectionClipboard = 36, - experimentalWhitespaceRendering = 37, - extraEditorClassName = 38, - fastScrollSensitivity = 39, - find = 40, - fixedOverflowWidgets = 41, - folding = 42, - foldingStrategy = 43, - foldingHighlight = 44, - foldingImportsByDefault = 45, - foldingMaximumRegions = 46, - unfoldOnClickAfterEndOfLine = 47, - fontFamily = 48, - fontInfo = 49, - fontLigatures = 50, - fontSize = 51, - fontWeight = 52, - fontVariations = 53, - formatOnPaste = 54, - formatOnType = 55, - glyphMargin = 56, - gotoLocation = 57, - hideCursorInOverviewRuler = 58, - hover = 59, - inDiffEditor = 60, - inlineSuggest = 61, - letterSpacing = 62, - lightbulb = 63, - lineDecorationsWidth = 64, - lineHeight = 65, - lineNumbers = 66, - lineNumbersMinChars = 67, - linkedEditing = 68, - links = 69, - matchBrackets = 70, - minimap = 71, - mouseStyle = 72, - mouseWheelScrollSensitivity = 73, - mouseWheelZoom = 74, - multiCursorMergeOverlapping = 75, - multiCursorModifier = 76, - multiCursorPaste = 77, - multiCursorLimit = 78, - occurrencesHighlight = 79, - overviewRulerBorder = 80, - overviewRulerLanes = 81, - padding = 82, - pasteAs = 83, - parameterHints = 84, - peekWidgetDefaultFocus = 85, - definitionLinkOpensInPeek = 86, - quickSuggestions = 87, - quickSuggestionsDelay = 88, - readOnly = 89, - readOnlyMessage = 90, - renameOnType = 91, - renderControlCharacters = 92, - renderFinalNewline = 93, - renderLineHighlight = 94, - renderLineHighlightOnlyWhenFocus = 95, - renderValidationDecorations = 96, - renderWhitespace = 97, - revealHorizontalRightPadding = 98, - roundedSelection = 99, - rulers = 100, - scrollbar = 101, - scrollBeyondLastColumn = 102, - scrollBeyondLastLine = 103, - scrollPredominantAxis = 104, - selectionClipboard = 105, - selectionHighlight = 106, - selectOnLineNumbers = 107, - showFoldingControls = 108, - showUnused = 109, - snippetSuggestions = 110, - smartSelect = 111, - smoothScrolling = 112, - stickyScroll = 113, - stickyTabStops = 114, - stopRenderingLineAfter = 115, - suggest = 116, - suggestFontSize = 117, - suggestLineHeight = 118, - suggestOnTriggerCharacters = 119, - suggestSelection = 120, - tabCompletion = 121, - tabIndex = 122, - unicodeHighlighting = 123, - unusualLineTerminators = 124, - useShadowDOM = 125, - useTabStops = 126, - wordBreak = 127, - wordSeparators = 128, - wordWrap = 129, - wordWrapBreakAfterCharacters = 130, - wordWrapBreakBeforeCharacters = 131, - wordWrapColumn = 132, - wordWrapOverride1 = 133, - wordWrapOverride2 = 134, - wrappingIndent = 135, - wrappingStrategy = 136, - showDeprecated = 137, - inlayHints = 138, - editorClassName = 139, - pixelRatio = 140, - tabFocusMode = 141, - layoutInfo = 142, - wrappingInfo = 143, - defaultColorDecorators = 144, - colorDecoratorsActivatedOn = 145, - inlineCompletionsAccessibilityVerbose = 146 + autoClosingComments = 7, + screenReaderAnnounceInlineSuggestion = 8, + autoClosingDelete = 9, + autoClosingOvertype = 10, + autoClosingQuotes = 11, + autoIndent = 12, + automaticLayout = 13, + autoSurround = 14, + bracketPairColorization = 15, + guides = 16, + codeLens = 17, + codeLensFontFamily = 18, + codeLensFontSize = 19, + colorDecorators = 20, + colorDecoratorsLimit = 21, + columnSelection = 22, + comments = 23, + contextmenu = 24, + copyWithSyntaxHighlighting = 25, + cursorBlinking = 26, + cursorSmoothCaretAnimation = 27, + cursorStyle = 28, + cursorSurroundingLines = 29, + cursorSurroundingLinesStyle = 30, + cursorWidth = 31, + disableLayerHinting = 32, + disableMonospaceOptimizations = 33, + domReadOnly = 34, + dragAndDrop = 35, + dropIntoEditor = 36, + emptySelectionClipboard = 37, + experimentalWhitespaceRendering = 38, + extraEditorClassName = 39, + fastScrollSensitivity = 40, + find = 41, + fixedOverflowWidgets = 42, + folding = 43, + foldingStrategy = 44, + foldingHighlight = 45, + foldingImportsByDefault = 46, + foldingMaximumRegions = 47, + unfoldOnClickAfterEndOfLine = 48, + fontFamily = 49, + fontInfo = 50, + fontLigatures = 51, + fontSize = 52, + fontWeight = 53, + fontVariations = 54, + formatOnPaste = 55, + formatOnType = 56, + glyphMargin = 57, + gotoLocation = 58, + hideCursorInOverviewRuler = 59, + hover = 60, + inDiffEditor = 61, + inlineSuggest = 62, + letterSpacing = 63, + lightbulb = 64, + lineDecorationsWidth = 65, + lineHeight = 66, + lineNumbers = 67, + lineNumbersMinChars = 68, + linkedEditing = 69, + links = 70, + matchBrackets = 71, + minimap = 72, + mouseStyle = 73, + mouseWheelScrollSensitivity = 74, + mouseWheelZoom = 75, + multiCursorMergeOverlapping = 76, + multiCursorModifier = 77, + multiCursorPaste = 78, + multiCursorLimit = 79, + occurrencesHighlight = 80, + overviewRulerBorder = 81, + overviewRulerLanes = 82, + padding = 83, + pasteAs = 84, + parameterHints = 85, + peekWidgetDefaultFocus = 86, + definitionLinkOpensInPeek = 87, + quickSuggestions = 88, + quickSuggestionsDelay = 89, + readOnly = 90, + readOnlyMessage = 91, + renameOnType = 92, + renderControlCharacters = 93, + renderFinalNewline = 94, + renderLineHighlight = 95, + renderLineHighlightOnlyWhenFocus = 96, + renderValidationDecorations = 97, + renderWhitespace = 98, + revealHorizontalRightPadding = 99, + roundedSelection = 100, + rulers = 101, + scrollbar = 102, + scrollBeyondLastColumn = 103, + scrollBeyondLastLine = 104, + scrollPredominantAxis = 105, + selectionClipboard = 106, + selectionHighlight = 107, + selectOnLineNumbers = 108, + showFoldingControls = 109, + showUnused = 110, + snippetSuggestions = 111, + smartSelect = 112, + smoothScrolling = 113, + stickyScroll = 114, + stickyTabStops = 115, + stopRenderingLineAfter = 116, + suggest = 117, + suggestFontSize = 118, + suggestLineHeight = 119, + suggestOnTriggerCharacters = 120, + suggestSelection = 121, + tabCompletion = 122, + tabIndex = 123, + unicodeHighlighting = 124, + unusualLineTerminators = 125, + useShadowDOM = 126, + useTabStops = 127, + wordBreak = 128, + wordSeparators = 129, + wordWrap = 130, + wordWrapBreakAfterCharacters = 131, + wordWrapBreakBeforeCharacters = 132, + wordWrapColumn = 133, + wordWrapOverride1 = 134, + wordWrapOverride2 = 135, + wrappingIndent = 136, + wrappingStrategy = 137, + showDeprecated = 138, + inlayHints = 139, + editorClassName = 140, + pixelRatio = 141, + tabFocusMode = 142, + layoutInfo = 143, + wrappingInfo = 144, + defaultColorDecorators = 145, + colorDecoratorsActivatedOn = 146, + inlineCompletionsAccessibilityVerbose = 147 } export const EditorOptions: { @@ -4848,6 +4854,7 @@ declare namespace monaco.editor { ariaRequired: IEditorOption; screenReaderAnnounceInlineSuggestion: IEditorOption; autoClosingBrackets: IEditorOption; + autoClosingComments: IEditorOption; autoClosingDelete: IEditorOption; autoClosingOvertype: IEditorOption; autoClosingQuotes: IEditorOption; From 88925c0478a5a0c22fcb13f46cb14a86bdc9ccbd Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 6 Sep 2023 14:31:52 -0700 Subject: [PATCH 581/607] more notebook suites --- .../test/browser/contrib/notebookOutline.test.ts | 2 ++ .../notebook/test/browser/notebookEditorModel.test.ts | 2 ++ .../test/browser/notebookKernelService.test.ts | 10 ++++++---- .../notebook/test/browser/notebookServiceImpl.test.ts | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts index 323abe63dda..e274246175e 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts @@ -17,8 +17,10 @@ import { IActiveNotebookEditor, INotebookEditorPane } from 'vs/workbench/contrib import { DisposableStore } from 'vs/base/common/lifecycle'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NotebookCellOutline } from 'vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Notebook Outline', function () { + ensureNoDisposablesAreLeakedInTestSuite(); let disposables: DisposableStore; let instantiationService: TestInstantiationService; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts index d350a742d9c..4abb19e1e25 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts @@ -10,6 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -20,6 +21,7 @@ import { INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } fro import { setupInstantiationService } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; suite('NotebookFileWorkingCopyModel', function () { + ensureNoDisposablesAreLeakedInTestSuite(); let disposables: DisposableStore; let instantiationService: TestInstantiationService; 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 37b67d8f526..7890c77c723 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -18,6 +18,7 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('NotebookKernelService', () => { @@ -26,6 +27,11 @@ suite('NotebookKernelService', () => { let disposables: DisposableStore; let onDidAddNotebookDocument: Emitter; + teardown(() => { + disposables.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); setup(function () { disposables = new DisposableStore(); @@ -52,10 +58,6 @@ suite('NotebookKernelService', () => { instantiationService.set(INotebookKernelService, kernelService); }); - teardown(() => { - disposables.dispose(); - }); - test('notebook priorities', function () { const u1 = URI.parse('foo:///one'); diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts index 225069a56d7..914567c3773 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts @@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IFileService } from 'vs/platform/files/common/files'; @@ -21,6 +22,7 @@ import { IExtensionService, nullExtensionDescription } from 'vs/workbench/servic import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; suite('NotebookProviderInfoStore', function () { + ensureNoDisposablesAreLeakedInTestSuite(); test('Can\'t open untitled notebooks in test #119363', function () { const disposables = new DisposableStore(); From f9169e658f9849bf30da64bf70d79bcb83259847 Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 6 Sep 2023 15:17:40 -0700 Subject: [PATCH 582/607] fix leaks, remove check from leaky insta service suite --- src/vs/workbench/api/test/browser/extHostNotebook.test.ts | 6 +++--- .../notebook/test/browser/contrib/notebookOutline.test.ts | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts index 569675861e7..11a4b961b60 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts +++ b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts @@ -148,7 +148,7 @@ suite('NotebookCell#Document', function () { const p = new Promise((resolve, reject) => { - extHostNotebookDocuments.onDidChangeNotebookDocument(e => { + disposables.add(extHostNotebookDocuments.onDidChangeNotebookDocument(e => { try { assert.strictEqual(e.contentChanges.length, 1); assert.strictEqual(e.contentChanges[0].addedCells.length, 2); @@ -168,7 +168,7 @@ suite('NotebookCell#Document', function () { } catch (err) { reject(err); } - }); + })); }); @@ -360,7 +360,7 @@ suite('NotebookCell#Document', function () { test('Opening a notebook results in VS Code firing the event onDidChangeActiveNotebookEditor twice #118470', function () { let count = 0; - extHostNotebooks.onDidChangeActiveNotebookEditor(() => count += 1); + disposables.add(extHostNotebooks.onDidChangeActiveNotebookEditor(() => count += 1)); extHostNotebooks.$acceptDocumentAndEditorsDelta(new SerializableObjectWithBuffers({ addedEditors: [{ diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts index e274246175e..323abe63dda 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts @@ -17,10 +17,8 @@ import { IActiveNotebookEditor, INotebookEditorPane } from 'vs/workbench/contrib import { DisposableStore } from 'vs/base/common/lifecycle'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { NotebookCellOutline } from 'vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline'; -import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Notebook Outline', function () { - ensureNoDisposablesAreLeakedInTestSuite(); let disposables: DisposableStore; let instantiationService: TestInstantiationService; From 258c8ca6dab454c79054c55c43c36cce2d9beafb Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 6 Sep 2023 15:40:18 -0700 Subject: [PATCH 583/607] fixed missed leaks --- .../api/test/browser/extHostNotebook.test.ts | 2 -- .../test/browser/extHostNotebookKernel.test.ts | 2 ++ .../browser/services/notebookServiceImpl.ts | 4 ++-- .../test/browser/notebookEditorModel.test.ts | 16 +++++++--------- .../test/browser/notebookServiceImpl.test.ts | 8 +++----- 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts index 11a4b961b60..7de8e1f9e75 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebook.test.ts +++ b/src/vs/workbench/api/test/browser/extHostNotebook.test.ts @@ -29,8 +29,6 @@ import { ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystem import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('NotebookCell#Document', function () { - - let rpcProtocol: TestRPCProtocol; let notebook: ExtHostNotebookDocument; let extHostDocumentsAndEditors: ExtHostDocumentsAndEditors; diff --git a/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts b/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts index d9448b421e9..0fabe6e6ac6 100644 --- a/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts +++ b/src/vs/workbench/api/test/browser/extHostNotebookKernel.test.ts @@ -52,7 +52,9 @@ suite('NotebookKernel', function () { teardown(function () { disposables.clear(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + setup(async function () { cellExecuteCreate.length = 0; cellExecuteUpdates.length = 0; diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index 0c82eb7cb7f..c01228ddc92 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -290,13 +290,13 @@ export class NotebookProviderInfoStore extends Disposable { mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); this._memento.saveMemento(); - return toDisposable(() => { + return this._register(toDisposable(() => { const mementoObject = this._memento.getMemento(StorageScope.PROFILE, StorageTarget.MACHINE); mementoObject[NotebookProviderInfoStore.CUSTOM_EDITORS_ENTRY_ID] = Array.from(this._contributedEditors.values()); this._memento.saveMemento(); editorRegistration?.dispose(); this._contributedEditors.delete(info.id); - }); + })); } getContributedNotebook(resource: URI): readonly NotebookProviderInfo[] { diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts index 4abb19e1e25..bbcd30876b1 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookEditorModel.test.ts @@ -10,7 +10,6 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; -import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -21,19 +20,18 @@ import { INotebookSerializer, INotebookService, SimpleNotebookProviderInfo } fro import { setupInstantiationService } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; suite('NotebookFileWorkingCopyModel', function () { - ensureNoDisposablesAreLeakedInTestSuite(); let disposables: DisposableStore; let instantiationService: TestInstantiationService; const configurationService = new TestConfigurationService(); - suiteSetup(() => { + teardown(() => disposables.dispose()); + + setup(() => { disposables = new DisposableStore(); instantiationService = setupInstantiationService(disposables); }); - suiteTeardown(() => disposables.dispose()); - test('no transient output is send to serializer', async function () { const notebook = instantiationService.createInstance(NotebookTextModel, @@ -46,7 +44,7 @@ suite('NotebookFileWorkingCopyModel', function () { { // transient output let callCount = 0; - const model = new NotebookFileWorkingCopyModel( + const model = disposables.add(new NotebookFileWorkingCopyModel( notebook, mockNotebookService(notebook, new class extends mock() { @@ -60,7 +58,7 @@ suite('NotebookFileWorkingCopyModel', function () { } ), configurationService - ); + )); await model.snapshot(CancellationToken.None); assert.strictEqual(callCount, 1); @@ -68,7 +66,7 @@ suite('NotebookFileWorkingCopyModel', function () { { // NOT transient output let callCount = 0; - const model = new NotebookFileWorkingCopyModel( + const model = disposables.add(new NotebookFileWorkingCopyModel( notebook, mockNotebookService(notebook, new class extends mock() { @@ -82,7 +80,7 @@ suite('NotebookFileWorkingCopyModel', function () { } ), configurationService - ); + )); await model.snapshot(CancellationToken.None); assert.strictEqual(callCount, 1); } diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts index 914567c3773..f8e70728176 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookServiceImpl.test.ts @@ -22,10 +22,9 @@ import { IExtensionService, nullExtensionDescription } from 'vs/workbench/servic import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; suite('NotebookProviderInfoStore', function () { - ensureNoDisposablesAreLeakedInTestSuite(); + const disposables = ensureNoDisposablesAreLeakedInTestSuite() as Pick; test('Can\'t open untitled notebooks in test #119363', function () { - const disposables = new DisposableStore(); const instantiationService = workbenchInstantiationService(undefined, disposables); const store = new NotebookProviderInfoStore( new class extends mock() { @@ -35,7 +34,7 @@ suite('NotebookProviderInfoStore', function () { new class extends mock() { override onDidRegisterExtensions = Event.None; }, - instantiationService.createInstance(EditorResolverService), + disposables.add(instantiationService.createInstance(EditorResolverService)), new TestConfigurationService(), new class extends mock() { override onDidChangeScreenReaderOptimized: Event = Event.None; @@ -46,6 +45,7 @@ suite('NotebookProviderInfoStore', function () { }, new class extends mock() { } ); + disposables.add(store); const fooInfo = new NotebookProviderInfo({ extension: nullExtensionDescription.identifier, @@ -89,8 +89,6 @@ suite('NotebookProviderInfoStore', function () { providers = store.getContributedNotebook(URI.parse('untitled:///test/nb.bar')); assert.strictEqual(providers.length, 1); assert.strictEqual(providers[0] === barInfo, true); - - disposables.dispose(); }); }); From e71d562fd84842720afea0932167934dd1c76159 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 6 Sep 2023 15:40:44 -0700 Subject: [PATCH 584/607] Show inline chat hint in empty text editors (#192352) --- .../browser/workbench.contribution.ts | 4 +- .../browser/accessibilityConfiguration.ts | 8 +-- .../browser/codeEditor.contribution.ts | 2 +- .../emptyTextEditorHint.css} | 4 +- .../emptyTextEditorHint.ts} | 66 ++++++++++++------- 5 files changed, 51 insertions(+), 33 deletions(-) rename src/vs/workbench/contrib/codeEditor/browser/{untitledTextEditorHint/untitledTextEditorHint.css => emptyTextEditorHint/emptyTextEditorHint.css} (80%) rename src/vs/workbench/contrib/codeEditor/browser/{untitledTextEditorHint/untitledTextEditorHint.ts => emptyTextEditorHint/emptyTextEditorHint.ts} (83%) diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index b3a23711702..4a5503dd6be 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -95,11 +95,11 @@ const registry = Registry.as(ConfigurationExtensions.Con key: 'untitledLabelFormat' }, "Controls the format of the label for an untitled editor."), }, - 'workbench.editor.untitled.hint': { + 'workbench.editor.empty.hint': { 'type': 'string', 'enum': ['text', 'hidden'], 'default': 'text', - 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'untitledHint' }, "Controls if the untitled text hint should be visible in the editor.") + 'markdownDescription': localize({ comment: ['This is the description for a setting. Values surrounded by single quotes are not to be translated.'], key: 'emptyEditorHint' }, "Controls if the empty editor text hint should be visible in the editor.") }, 'workbench.editor.languageDetection': { type: 'boolean', diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 384572c7402..97ae6dacb4a 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -42,7 +42,7 @@ export const enum AccessibilityVerbositySettingId { Editor = 'accessibility.verbosity.editor', Hover = 'accessibility.verbosity.hover', Notification = 'accessibility.verbosity.notification', - EditorUntitledHint = 'accessibility.verbosity.untitledHint' + EmptyEditorHint = 'accessibility.verbosity.emptyEditorHint' } export const enum AccessibleViewProviderId { @@ -56,7 +56,7 @@ export const enum AccessibleViewProviderId { Editor = 'editor', Hover = 'hover', Notification = 'notification', - EditorUntitledHint = 'editor.untitledHint' + EmptyEditorHint = 'emptyEditorHint' } const baseProperty: object = { @@ -106,8 +106,8 @@ const configuration: IConfigurationNode = { description: localize('verbosity.notification', 'Provide information about how to open the notification in an accessible view.'), ...baseProperty }, - [AccessibilityVerbositySettingId.EditorUntitledHint]: { - description: localize('verbosity.untitledhint', 'Provide information about relevant actions in an untitled text editor.'), + [AccessibilityVerbositySettingId.EmptyEditorHint]: { + description: localize('verbosity.emptyEditorHint', 'Provide information about relevant actions in an empty text editor.'), ...baseProperty } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts index 4c8f65b1588..b858435378c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/codeEditor.contribution.ts @@ -20,6 +20,6 @@ import './toggleMultiCursorModifier'; import './toggleRenderControlCharacter'; import './toggleRenderWhitespace'; import './toggleWordWrap'; -import './untitledTextEditorHint/untitledTextEditorHint'; +import './emptyTextEditorHint/emptyTextEditorHint'; import './workbenchReferenceSearch'; import './editorLineNumberMenu'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.css b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.css similarity index 80% rename from src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.css rename to src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.css index 538b988c4b3..649545a875c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.css +++ b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.css @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .contentWidgets .untitled-hint { +.monaco-editor .contentWidgets .empty-editor-hint { color: var(--vscode-input-placeholderForeground); } -.monaco-editor .contentWidgets .untitled-hint a { +.monaco-editor .contentWidgets .empty-editor-hint a { color: var(--vscode-textLink-foreground) } diff --git a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts similarity index 83% rename from src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts rename to src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts index b2dd6a3cdea..4be8c09a49f 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/untitledTextEditorHint/untitledTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./untitledTextEditorHint'; +import 'vs/css!./emptyTextEditorHint'; import * as dom from 'vs/base/browser/dom'; import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; @@ -30,16 +30,33 @@ import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLa import { OS } from 'vs/base/common/platform'; import { status } from 'vs/base/browser/ui/aria/aria'; import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; const $ = dom.$; -const untitledTextEditorHintSetting = 'workbench.editor.untitled.hint'; -export class UntitledTextEditorHintContribution implements IEditorContribution { +// TODO@joyceerhl remove this after a few iterations +Registry.as(Extensions.ConfigurationMigration) + .registerConfigurationMigrations([{ + key: 'workbench.editor.untitled.hint', + migrateFn: (value, _accessor) => ([ + [emptyTextEditorHintSetting, { value }], + ]) + }, + { + key: 'accessibility.verbosity.untitledHint', + migrateFn: (value, _accessor) => ([ + [AccessibilityVerbositySettingId.EmptyEditorHint, { value }], + ]) + }]); - public static readonly ID = 'editor.contrib.untitledTextEditorHint'; +const emptyTextEditorHintSetting = 'workbench.editor.empty.hint'; +export class EmptyTextEditorHintContribution implements IEditorContribution { + + public static readonly ID = 'editor.contrib.emptyTextEditorHint'; private toDispose: IDisposable[]; - private untitledTextHintContentWidget: UntitledTextEditorHintContentWidget | undefined; + private textHintContentWidget: EmptyTextEditorHintContentWidget | undefined; constructor( private readonly editor: ICodeEditor, @@ -56,13 +73,13 @@ export class UntitledTextEditorHintContribution implements IEditorContribution { this.toDispose.push(this.editor.onDidChangeModel(() => this.update())); this.toDispose.push(this.editor.onDidChangeModelLanguage(() => this.update())); this.toDispose.push(this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(untitledTextEditorHintSetting)) { + if (e.affectsConfiguration(emptyTextEditorHintSetting)) { this.update(); } })); this.toDispose.push(inlineChatSessionService.onWillStartSession(editor => { if (this.editor === editor) { - this.untitledTextHintContentWidget?.dispose(); + this.textHintContentWidget?.dispose(); } })); this.toDispose.push(inlineChatSessionService.onDidEndSession(editor => { @@ -73,15 +90,16 @@ export class UntitledTextEditorHintContribution implements IEditorContribution { } private update(): void { - this.untitledTextHintContentWidget?.dispose(); - const configValue = this.configurationService.getValue(untitledTextEditorHintSetting); + this.textHintContentWidget?.dispose(); + const configValue = this.configurationService.getValue(emptyTextEditorHintSetting); const model = this.editor.getModel(); const inlineChatProviders = [...this.inlineChatService.getAllProvider()]; - const shouldRenderEitherDefaultOrInlineChatHint = model?.getLanguageId() === PLAINTEXT_LANGUAGE_ID && !inlineChatProviders.length || inlineChatProviders.length > 0; + const shouldRenderInlineChatHint = inlineChatProviders.length > 0; + const shouldRenderDefaultHint = model?.getLanguageId() === PLAINTEXT_LANGUAGE_ID && !inlineChatProviders.length; - if (model && model.uri.scheme === Schemas.untitled && shouldRenderEitherDefaultOrInlineChatHint && configValue === 'text') { - this.untitledTextHintContentWidget = new UntitledTextEditorHintContentWidget( + if (model && (model.uri.scheme === Schemas.untitled && shouldRenderDefaultHint || shouldRenderInlineChatHint) && configValue !== 'hidden') { + this.textHintContentWidget = new EmptyTextEditorHintContentWidget( this.editor, this.editorGroupsService, this.commandService, @@ -96,13 +114,13 @@ export class UntitledTextEditorHintContribution implements IEditorContribution { dispose(): void { dispose(this.toDispose); - this.untitledTextHintContentWidget?.dispose(); + this.textHintContentWidget?.dispose(); } } -class UntitledTextEditorHintContentWidget implements IContentWidget { +class EmptyTextEditorHintContentWidget implements IContentWidget { - private static readonly ID = 'editor.widget.untitledHint'; + private static readonly ID = 'editor.widget.emptyHint'; private domNode: HTMLElement | undefined; private toDispose: DisposableStore; @@ -129,7 +147,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { })); const onDidFocusEditorText = Event.debounce(this.editor.onDidFocusEditorText, () => undefined, 500); this.toDispose.add(onDidFocusEditorText(() => { - if (this.editor.hasTextFocus() && this.isVisible && this.ariaLabel && this.configurationService.getValue(AccessibilityVerbositySettingId.EditorUntitledHint)) { + if (this.editor.hasTextFocus() && this.isVisible && this.ariaLabel && this.configurationService.getValue(AccessibilityVerbositySettingId.EmptyEditorHint)) { status(this.ariaLabel); } })); @@ -147,7 +165,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { } getId(): string { - return UntitledTextEditorHintContentWidget.ID; + return EmptyTextEditorHintContentWidget.ID; } private _getHintInlineChat(providers: IInlineChatSessionProvider[]) { @@ -175,14 +193,14 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { } }; - const hintElement = $('untitled-hint-text'); + const hintElement = $('empty-hint-text'); hintElement.style.display = 'block'; const keybindingHint = this.keybindingService.lookupKeybinding(inlineChatId); const keybindingHintLabel = keybindingHint?.getLabel(); if (keybindingHint && keybindingHintLabel) { - const actionPart = localize('untitledText', 'Press {0} to ask {1} to do something. ', keybindingHintLabel, providerName); + const actionPart = localize('emptyHintText', 'Press {0} to ask {1} to do something. ', keybindingHintLabel, providerName); const [before, after] = actionPart.split(keybindingHintLabel).map((fragment) => { const hintPart = $('a', undefined, fragment); @@ -203,7 +221,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { hintElement.appendChild(after); - const typeToDismiss = localize('untitledText2', 'Start typing to dismiss.'); + const typeToDismiss = localize('emptyHintTextDismiss', 'Start typing to dismiss.'); const textHint2 = $('span', undefined, typeToDismiss); textHint2.style.fontStyle = 'italic'; hintElement.appendChild(textHint2); @@ -284,7 +302,7 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { }; const dontShowOnClickOrTap = () => { - this.configurationService.updateValue(untitledTextEditorHintSetting, 'hidden'); + this.configurationService.updateValue(emptyTextEditorHintSetting, 'hidden'); this.dispose(); this.editor.focus(); }; @@ -318,14 +336,14 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { getDomNode(): HTMLElement { if (!this.domNode) { - this.domNode = $('.untitled-hint'); + this.domNode = $('.empty-editor-hint'); this.domNode.style.width = 'max-content'; this.domNode.style.paddingLeft = '4px'; const inlineChatProviders = [...this.inlineChatService.getAllProvider()]; const { hintElement, ariaLabel } = !inlineChatProviders.length ? this._getHintDefault() : this._getHintInlineChat(inlineChatProviders); this.domNode.append(hintElement); - this.ariaLabel = ariaLabel.concat(localize('disableHint', ' Toggle {0} in settings to disable this hint.', AccessibilityVerbositySettingId.EditorUntitledHint)); + this.ariaLabel = ariaLabel.concat(localize('disableHint', ' Toggle {0} in settings to disable this hint.', AccessibilityVerbositySettingId.EmptyEditorHint)); this.toDispose.add(dom.addDisposableListener(this.domNode, 'click', () => { this.editor.focus(); @@ -350,4 +368,4 @@ class UntitledTextEditorHintContentWidget implements IContentWidget { } } -registerEditorContribution(UntitledTextEditorHintContribution.ID, UntitledTextEditorHintContribution, EditorContributionInstantiation.Eager); // eager because it needs to render a help message +registerEditorContribution(EmptyTextEditorHintContribution.ID, EmptyTextEditorHintContribution, EditorContributionInstantiation.Eager); // eager because it needs to render a help message From 95399e55edd76e483731686282518b28555932d2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:41:51 -0700 Subject: [PATCH 585/607] Finish terminal unit test leak --- .../test/browser/terminalLinkOpeners.test.ts | 19 ++++++------ .../test/browser/terminalLinkParsing.test.ts | 3 ++ .../browser/terminalLocalLinkDetector.test.ts | 19 ++++++------ .../terminalMultiLineLinkDetector.test.ts | 13 ++++---- .../browser/terminalUriLinkDetector.test.ts | 5 +++- .../browser/terminalWordLinkDetector.test.ts | 13 ++++---- .../test/browser/quickFixAddon.test.ts | 30 ++++++++++--------- 7 files changed, 55 insertions(+), 47 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts index e9193758069..c55b7caff8b 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkOpeners.test.ts @@ -27,6 +27,7 @@ import { IFileQuery, ISearchComplete, ISearchService } from 'vs/workbench/servic import { SearchService } from 'vs/workbench/services/search/common/searchService'; import { ITerminalLogService, ITerminalOutputMatcher } from 'vs/platform/terminal/common/terminal'; import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; interface ITerminalLinkActivationResult { source: 'editor' | 'search'; @@ -70,6 +71,8 @@ class TestTerminalSearchLinkOpener extends TerminalSearchLinkOpener { } suite('Workbench - TerminalLinkOpeners', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let fileService: TestFileService; let searchService: TestSearchService; @@ -77,9 +80,9 @@ suite('Workbench - TerminalLinkOpeners', () => { let xterm: Terminal; setup(async () => { - instantiationService = new TestInstantiationService(); - fileService = new TestFileService(new NullLogService()); - searchService = new TestSearchService(null!, null!, null!, null!, null!, null!, null!); + instantiationService = store.add(new TestInstantiationService()); + fileService = store.add(new TestFileService(new NullLogService())); + searchService = store.add(new TestSearchService(null!, null!, null!, null!, null!, null!, null!)); instantiationService.set(IFileService, fileService); instantiationService.set(ILogService, new NullLogService()); instantiationService.set(ISearchService, searchService); @@ -110,11 +113,7 @@ suite('Workbench - TerminalLinkOpeners', () => { } } as Partial); const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - xterm = new TerminalCtor({ allowProposedApi: true }); - }); - - teardown(() => { - instantiationService.dispose(); + xterm = store.add(new TerminalCtor({ allowProposedApi: true })); }); suite('TerminalSearchLinkOpener', () => { @@ -124,8 +123,8 @@ suite('Workbench - TerminalLinkOpeners', () => { let localFileOpener: TerminalLocalFileLinkOpener; setup(() => { - capabilities = new TerminalCapabilityStore(); - commandDetection = instantiationService.createInstance(TestCommandDetectionCapability, xterm); + capabilities = store.add(new TerminalCapabilityStore()); + commandDetection = store.add(instantiationService.createInstance(TestCommandDetectionCapability, xterm)); capabilities.add(TerminalCapability.CommandDetection, commandDetection); }); diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts index 453b1f95ec4..54694c34f01 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLinkParsing.test.ts @@ -5,6 +5,7 @@ import { deepStrictEqual, ok, strictEqual } from 'assert'; import { OperatingSystem } from 'vs/base/common/platform'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { detectLinks, detectLinkSuffixes, getLinkSuffix, IParsedLink, removeLinkQueryString, removeLinkSuffix } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkParsing'; interface ITestLink { @@ -142,6 +143,8 @@ const testLinks: ITestLink[] = [ const testLinksWithSuffix = testLinks.filter(e => !!e.suffix); suite('TerminalLinkParsing', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + suite('removeLinkSuffix', () => { for (const testLink of testLinks) { test('`' + testLink.link + '`', () => { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts index ba2e367c310..4e7cfa28cf6 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalLocalLinkDetector.test.ts @@ -22,6 +22,7 @@ import { URI } from 'vs/base/common/uri'; import { NullLogService } from 'vs/platform/log/common/log'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const unixLinks: (string | { link: string; resource: URI })[] = [ // Absolute @@ -145,6 +146,8 @@ const supportedFallbackLinkFormats: LinkFormatInfo[] = [ ]; suite('Workbench - TerminalLocalLinkDetector', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let detector: TerminalLocalLinkDetector; @@ -157,11 +160,13 @@ suite('Workbench - TerminalLocalLinkDetector', () => { text: string, expected: ({ uri: URI; range: [number, number][] })[] ) { + let to; const race = await Promise.race([ assertLinkHelper(text, expected, detector, type).then(() => 'success'), - timeout(2).then(() => 'timeout') + (to = timeout(2)).then(() => 'timeout') ]); strictEqual(race, 'success', `Awaiting link assertion for "${text}" timed out`); + to.cancel(); } async function assertLinksWithWrapped(link: string, resource?: URI) { @@ -173,7 +178,7 @@ suite('Workbench - TerminalLocalLinkDetector', () => { } setup(async () => { - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IFileService, { @@ -192,13 +197,9 @@ suite('Workbench - TerminalLocalLinkDetector', () => { xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); }); - teardown(() => { - instantiationService.dispose(); - }); - suite('platform independent', () => { setup(() => { - detector = instantiationService.createInstance(TerminalLocalLinkDetector, xterm, new TerminalCapabilityStore(), { + detector = instantiationService.createInstance(TerminalLocalLinkDetector, xterm, store.add(new TerminalCapabilityStore()), { initialCwd: '/parent/cwd', os: OperatingSystem.Linux, remoteAuthority: undefined, @@ -235,7 +236,7 @@ suite('Workbench - TerminalLocalLinkDetector', () => { suite('macOS/Linux', () => { setup(() => { - detector = instantiationService.createInstance(TerminalLocalLinkDetector, xterm, new TerminalCapabilityStore(), { + detector = instantiationService.createInstance(TerminalLocalLinkDetector, xterm, store.add(new TerminalCapabilityStore()), { initialCwd: '/parent/cwd', os: OperatingSystem.Linux, remoteAuthority: undefined, @@ -277,7 +278,7 @@ suite('Workbench - TerminalLocalLinkDetector', () => { const wslUnixToWindowsPathMap: Map = new Map(); setup(() => { - detector = instantiationService.createInstance(TerminalLocalLinkDetector, xterm, new TerminalCapabilityStore(), { + detector = instantiationService.createInstance(TerminalLocalLinkDetector, xterm, store.add(new TerminalCapabilityStore()), { initialCwd: 'C:\\Parent\\Cwd', os: OperatingSystem.Windows, remoteAuthority: undefined, diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts index a0c84d878b2..e7725906b21 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalMultiLineLinkDetector.test.ts @@ -21,6 +21,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import { TerminalMultiLineLinkDetector } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalMultiLineLinkDetector'; import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const unixLinks: (string | { link: string; resource: URI })[] = [ // Absolute @@ -100,6 +101,8 @@ const supportedLinkFormats: LinkFormatInfo[] = [ ]; suite('Workbench - TerminalMultiLineLinkDetector', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let detector: TerminalMultiLineLinkDetector; @@ -112,11 +115,13 @@ suite('Workbench - TerminalMultiLineLinkDetector', () => { text: string, expected: ({ uri: URI; range: [number, number][] })[] ) { + let to; const race = await Promise.race([ assertLinkHelper(text, expected, detector, type).then(() => 'success'), - timeout(2).then(() => 'timeout') + (to = timeout(2)).then(() => 'timeout') ]); strictEqual(race, 'success', `Awaiting link assertion for "${text}" timed out`); + to.cancel(); } async function assertLinksMain(link: string, resource?: URI) { @@ -132,7 +137,7 @@ suite('Workbench - TerminalMultiLineLinkDetector', () => { } setup(async () => { - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IFileService, { @@ -151,10 +156,6 @@ suite('Workbench - TerminalMultiLineLinkDetector', () => { xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); }); - teardown(() => { - instantiationService.dispose(); - }); - suite('macOS/Linux', () => { setup(() => { detector = instantiationService.createInstance(TerminalMultiLineLinkDetector, xterm, { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts index f42e8619756..d4adc4f38f0 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalUriLinkDetector.test.ts @@ -16,8 +16,11 @@ import { URI } from 'vs/base/common/uri'; import type { Terminal } from 'xterm'; import { OperatingSystem } from 'vs/base/common/platform'; import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Workbench - TerminalUriLinkDetector', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let configurationService: TestConfigurationService; let detector: TerminalUriLinkDetector; let xterm: Terminal; @@ -25,7 +28,7 @@ suite('Workbench - TerminalUriLinkDetector', () => { let instantiationService: TestInstantiationService; setup(async () => { - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); configurationService = new TestConfigurationService(); instantiationService.stub(IConfigurationService, configurationService); instantiationService.stub(IFileService, { diff --git a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts index bdb6dc8ac97..c30d174f9ff 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/test/browser/terminalWordLinkDetector.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; @@ -15,13 +16,15 @@ import { TestProductService } from 'vs/workbench/test/common/workbenchTestServic import type { Terminal } from 'xterm'; suite('Workbench - TerminalWordLinkDetector', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let configurationService: TestConfigurationService; let detector: TerminalWordLinkDetector; let xterm: Terminal; let instantiationService: TestInstantiationService; setup(async () => { - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); configurationService = new TestConfigurationService(); await configurationService.setUserConfiguration('terminal', { integrated: { wordSeparators: '' } }); @@ -29,12 +32,8 @@ suite('Workbench - TerminalWordLinkDetector', () => { instantiationService.set(IProductService, TestProductService); const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - xterm = new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 }); - detector = instantiationService.createInstance(TerminalWordLinkDetector, xterm); - }); - - teardown(() => { - instantiationService.dispose(); + xterm = store.add(new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 })); + detector = store.add(instantiationService.createInstance(TerminalWordLinkDetector, xterm)); }); async function assertLink( diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts index 74d2ae5cd88..44f01d96083 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/test/browser/quickFixAddon.test.ts @@ -19,7 +19,7 @@ import { gitSimilar, freePort, FreePortOutputRegex, gitCreatePr, GitCreatePrOutp import { TerminalQuickFixAddon, getQuickFixesForCommand } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon'; import { URI } from 'vs/base/common/uri'; import type { Terminal } from 'xterm'; -import { Emitter } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { LabelService } from 'vs/workbench/services/label/common/labelService'; import { ILabelService } from 'vs/platform/label/common/label'; import { OpenerService } from 'vs/editor/browser/services/openerService'; @@ -30,8 +30,11 @@ import { ITerminalQuickFixService } from 'vs/workbench/contrib/terminalContrib/q import { ITerminalOutputMatcher } from 'vs/platform/terminal/common/terminal'; import { importAMDNodeModule } from 'vs/amdX'; import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('QuickFixAddon', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let quickFixAddon: TerminalQuickFixAddon; let commandDetection: CommandDetectionCapability; let commandService: TestCommandService; @@ -39,37 +42,36 @@ suite('QuickFixAddon', () => { let labelService: LabelService; let terminal: Terminal; let instantiationService: TestInstantiationService; + setup(async () => { - instantiationService = new TestInstantiationService(); + instantiationService = store.add(new TestInstantiationService()); const TerminalCtor = (await importAMDNodeModule('xterm', 'lib/xterm.js')).Terminal; - terminal = new TerminalCtor({ + terminal = store.add(new TerminalCtor({ allowProposedApi: true, cols: 80, rows: 30 - }); - instantiationService.stub(IStorageService, new TestStorageService()); + })); + instantiationService.stub(IStorageService, store.add(new TestStorageService())); instantiationService.stub(ITerminalQuickFixService, { - onDidRegisterProvider: new Emitter().event, - onDidUnregisterProvider: new Emitter().event, - onDidRegisterCommandSelector: new Emitter().event, + onDidRegisterProvider: Event.None, + onDidUnregisterProvider: Event.None, + onDidRegisterCommandSelector: Event.None, extensionQuickFixes: Promise.resolve([]) } as Partial); instantiationService.stub(IConfigurationService, new TestConfigurationService()); instantiationService.stub(ILabelService, {} as Partial); - const capabilities = new TerminalCapabilityStore(); + const capabilities = store.add(new TerminalCapabilityStore()); instantiationService.stub(ILogService, new NullLogService()); - commandDetection = instantiationService.createInstance(CommandDetectionCapability, terminal); + commandDetection = store.add(instantiationService.createInstance(CommandDetectionCapability, terminal)); capabilities.add(TerminalCapability.CommandDetection, commandDetection); - instantiationService.stub(IContextMenuService, instantiationService.createInstance(ContextMenuService)); + instantiationService.stub(IContextMenuService, store.add(instantiationService.createInstance(ContextMenuService))); instantiationService.stub(IOpenerService, {} as Partial); commandService = new TestCommandService(instantiationService); quickFixAddon = instantiationService.createInstance(TerminalQuickFixAddon, [], capabilities); terminal.loadAddon(quickFixAddon); }); - teardown(() => { - instantiationService.dispose(); - }); + suite('registerCommandFinishedListener & getMatchActions', () => { suite('gitSimilarCommand', () => { const expectedMap = new Map(); From dad3abe3e76292388688702d77d8838d721f6c39 Mon Sep 17 00:00:00 2001 From: aamunger Date: Wed, 6 Sep 2023 15:58:42 -0700 Subject: [PATCH 586/607] removed failing check --- .../notebook/test/browser/notebookKernelService.test.ts | 3 --- 1 file changed, 3 deletions(-) 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 7890c77c723..c964f7b59da 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookKernelService.test.ts @@ -18,7 +18,6 @@ import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/no import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { TransientOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('NotebookKernelService', () => { @@ -31,8 +30,6 @@ suite('NotebookKernelService', () => { disposables.dispose(); }); - ensureNoDisposablesAreLeakedInTestSuite(); - setup(function () { disposables = new DisposableStore(); From 9c17df54bd27cf9976e30a09749519bfc4e3a815 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Thu, 7 Sep 2023 12:20:07 +1000 Subject: [PATCH 587/607] Ensure orig_nbformat is not written to ipynb file (#192359) --- extensions/ipynb/src/ipynbMain.ts | 6 ++---- extensions/ipynb/src/notebookSerializer.ts | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/extensions/ipynb/src/ipynbMain.ts b/extensions/ipynb/src/ipynbMain.ts index 1d61f8a1cae..c256e3b4f65 100644 --- a/extensions/ipynb/src/ipynbMain.ts +++ b/extensions/ipynb/src/ipynbMain.ts @@ -24,7 +24,7 @@ type NotebookMetadata = { pygments_lexer?: string; [propName: string]: unknown; }; - orig_nbformat: number; + orig_nbformat?: number; [propName: string]: unknown; }; @@ -76,9 +76,7 @@ export function activate(context: vscode.ExtensionContext) { data.metadata = { custom: { cells: [], - metadata: { - orig_nbformat: 4 - }, + metadata: {}, nbformat: 4, nbformat_minor: 2 } diff --git a/extensions/ipynb/src/notebookSerializer.ts b/extensions/ipynb/src/notebookSerializer.ts index 968c2738ed4..26cc2b44232 100644 --- a/extensions/ipynb/src/notebookSerializer.ts +++ b/extensions/ipynb/src/notebookSerializer.ts @@ -63,7 +63,7 @@ export class NotebookSerializer implements vscode.NotebookSerializer { // For notebooks without metadata default the language in metadata to the preferred language. if (!json.metadata || (!json.metadata.kernelspec && !json.metadata.language_info)) { - json.metadata = json.metadata || { orig_nbformat: defaultNotebookFormat.major }; + json.metadata = json.metadata || {}; json.metadata.language_info = json.metadata.language_info || { name: preferredCellLanguage }; } @@ -101,8 +101,8 @@ export class NotebookSerializer implements vscode.NotebookSerializer { export function getNotebookMetadata(document: vscode.NotebookDocument | vscode.NotebookData) { const notebookContent: Partial = document.metadata?.custom || {}; notebookContent.cells = notebookContent.cells || []; - notebookContent.nbformat = notebookContent.nbformat || 4; - notebookContent.nbformat_minor = notebookContent.nbformat_minor ?? 2; - notebookContent.metadata = notebookContent.metadata || { orig_nbformat: 4 }; + notebookContent.nbformat = notebookContent.nbformat || defaultNotebookFormat.major; + notebookContent.nbformat_minor = notebookContent.nbformat_minor ?? defaultNotebookFormat.minor; + notebookContent.metadata = notebookContent.metadata || {}; return notebookContent; } From 8adddf5d4839b1c7af016994cb0ad0803c7bfa99 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 7 Sep 2023 09:30:19 +0200 Subject: [PATCH 588/607] debt - dispose things in my unit tests (#192112) * debt - dispose in `WorkingCopyHistoryTracker` * adopt more * more * more * more * fix * fix * input * input * input * input * input * input * input * input * wtf * . * . * inp * . * fix compile error * fux * give up * fixup disposable * testing: fix cannot read properties of undefined (reading 'layout') (#192340) Fixes #189326 * input --------- Co-authored-by: Connor Peet --- src/vs/base/common/stream.ts | 4 + src/vs/editor/common/model.ts | 2 +- src/vs/platform/files/common/fileService.ts | 5 +- .../storage/electron-main/storageMain.ts | 4 +- .../electron-main/storageMainService.ts | 18 +-- .../test/common/storageService.test.ts | 17 ++- .../electron-main/storageMainService.test.ts | 66 ++++---- .../browser/parts/editor/editorAutoSave.ts | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 4 +- .../extensionsActions.test.ts | 5 +- .../files/test/browser/editorAutoSave.test.ts | 32 ++-- .../test/browser/fileEditorInput.test.ts | 5 +- .../browser/textFileEditorTracker.test.ts | 47 +++--- .../test/browser/testNotebookEditor.ts | 5 +- .../testing/browser/testingExplorerView.ts | 2 +- .../editor/test/browser/editorService.test.ts | 17 +-- .../extensionEnablementService.test.ts | 8 +- ...extensionManifestPropertiesService.test.ts | 3 +- .../test/browser/historyService.test.ts | 2 +- .../electron-sandbox/lifecycleService.test.ts | 6 +- .../test/browser/storageService.test.ts | 41 +++-- .../browser/browserTextFileService.io.test.ts | 16 +- .../test/browser/textFileEditorModel.test.ts | 7 +- .../textFileEditorModelManager.test.ts | 5 +- .../test/browser/textFileService.test.ts | 7 +- .../test/common/textFileService.io.test.ts | 32 ++-- .../nativeTextFileService.io.test.ts | 13 +- .../nativeTextFileService.test.ts | 19 ++- .../browser/textModelResolverService.test.ts | 9 +- .../test/browser/untitledTextEditor.test.ts | 7 +- .../common/workingCopyBackupService.ts | 12 +- .../common/workingCopyBackupTracker.ts | 63 +++++--- .../common/workingCopyHistoryService.ts | 2 +- .../workingCopyBackupService.ts | 2 +- .../browser/fileWorkingCopyManager.test.ts | 10 +- .../test/browser/resourceWorkingCopy.test.ts | 8 +- .../browser/storedFileWorkingCopy.test.ts | 16 +- .../storedFileWorkingCopyManager.test.ts | 10 +- .../browser/untitledFileWorkingCopy.test.ts | 8 +- .../untitledFileWorkingCopyManager.test.ts | 10 +- .../untitledScratchpadWorkingCopy.test.ts | 8 +- .../browser/workingCopyBackupTracker.test.ts | 77 ++++------ .../browser/workingCopyEditorService.test.ts | 26 ++-- .../browser/workingCopyFileService.test.ts | 7 +- .../workingCopyBackupService.test.ts | 12 +- .../workingCopyBackupTracker.test.ts | 47 +++--- .../workingCopyHistoryService.test.ts | 114 +++++++------- .../workingCopyHistoryTracker.test.ts | 99 ++++-------- .../test/common/testWorkspaceTrustService.ts | 144 ------------------ .../test/common/workspaceTrust.test.ts | 3 +- .../parts/editor/diffEditorInput.test.ts | 8 +- .../test/browser/parts/editor/editor.test.ts | 45 +++--- .../parts/editor/editorDiffModel.test.ts | 5 +- .../browser/parts/editor/editorInput.test.ts | 5 +- .../browser/parts/editor/editorPane.test.ts | 96 ++++++------ .../parts/editor/resourceEditorInput.test.ts | 5 +- .../editor/sideBySideEditorInput.test.ts | 8 +- .../parts/editor/textEditorPane.test.ts | 15 +- .../editor/textResourceEditorInput.test.ts | 6 +- .../test/browser/workbenchTestServices.ts | 64 +++++--- .../test/common/workbenchTestServices.ts | 139 +++++++++++++++++ .../electron-sandbox/workbenchTestServices.ts | 18 +-- 62 files changed, 755 insertions(+), 747 deletions(-) delete mode 100644 src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts diff --git a/src/vs/base/common/stream.ts b/src/vs/base/common/stream.ts index 055558fc748..d6cd674b1de 100644 --- a/src/vs/base/common/stream.ts +++ b/src/vs/base/common/stream.ts @@ -622,11 +622,15 @@ export function peekStream(stream: ReadableStream, maxChunks: number): Pro // Error Listener const errorListener = (error: Error) => { + streamListeners.dispose(); + return reject(error); }; // End Listener const endListener = () => { + streamListeners.dispose(); + return resolve({ stream, buffer, ended: true }); }; diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index ea74d5c19b9..3d9f01f7dfa 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -1395,7 +1395,7 @@ export class SearchData { /** * @internal */ -export interface ITextBuffer extends IReadonlyTextBuffer { +export interface ITextBuffer extends IReadonlyTextBuffer, IDisposable { setEOL(newEOL: '\r\n' | '\n'): void; applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult; } diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index e88ff723b8b..b65c4e8705a 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -537,7 +537,7 @@ export class FileService extends Disposable implements IFileService { // validate read operation const statPromise = this.validateReadFile(resource, options).then(stat => stat, error => { - cancellableSource.cancel(); + cancellableSource.dispose(true); throw error; }); @@ -572,6 +572,9 @@ export class FileService extends Disposable implements IFileService { fileStream = this.readFileBuffered(provider, resource, cancellableSource.token, options); } + fileStream.on('end', () => cancellableSource.dispose()); + fileStream.on('error', () => cancellableSource.dispose()); + const fileStat = await statPromise; return { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 6843793692f..c110f28015b 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -122,7 +122,7 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { private readonly _onDidCloseStorage = this._register(new Emitter()); readonly onDidCloseStorage = this._onDidCloseStorage.event; - private _storage = new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY }); // storage is in-memory until initialized + private _storage = this._register(new Storage(new InMemoryStorageDatabase(), { hint: StorageHint.STORAGE_IN_MEMORY })); // storage is in-memory until initialized get storage(): IStorage { return this._storage; } abstract get path(): string | undefined; @@ -155,7 +155,7 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { try { // Create storage via subclasses - const storage = await this.doCreate(); + const storage = this._register(await this.doCreate()); // Replace our in-memory storage with the real // once as soon as possible without awaiting diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index bdfad4eacb6..4d5c2eb209a 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -164,16 +164,16 @@ export class StorageMainService extends Disposable implements IStorageMainServic //#region Application Storage - readonly applicationStorage = this.createApplicationStorage(); + readonly applicationStorage = this._register(this.createApplicationStorage()); private createApplicationStorage(): IStorageMain { this.logService.trace(`StorageMainService: creating application storage`); const applicationStorage = new ApplicationStorageMain(this.getStorageOptions(), this.userDataProfilesService, this.logService, this.fileService); - once(applicationStorage.onDidCloseStorage)(() => { + this._register(once(applicationStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed application storage`); - }); + })); return applicationStorage; } @@ -193,7 +193,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic if (!profileStorage) { this.logService.trace(`StorageMainService: creating profile storage (${profile.name})`); - profileStorage = this.createProfileStorage(profile); + profileStorage = this._register(this.createProfileStorage(profile)); this.mapProfileToStorage.set(profile.id, profileStorage); const listener = this._register(profileStorage.onDidChangeStorage(e => this._onDidChangeProfileStorage.fire({ @@ -202,12 +202,12 @@ export class StorageMainService extends Disposable implements IStorageMainServic profile }))); - once(profileStorage.onDidCloseStorage)(() => { + this._register(once(profileStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed profile storage (${profile.name})`); this.mapProfileToStorage.delete(profile.id); listener.dispose(); - }); + })); } return profileStorage; @@ -238,14 +238,14 @@ export class StorageMainService extends Disposable implements IStorageMainServic if (!workspaceStorage) { this.logService.trace(`StorageMainService: creating workspace storage (${workspace.id})`); - workspaceStorage = this.createWorkspaceStorage(workspace); + workspaceStorage = this._register(this.createWorkspaceStorage(workspace)); this.mapWorkspaceToStorage.set(workspace.id, workspaceStorage); - once(workspaceStorage.onDidCloseStorage)(() => { + this._register(once(workspaceStorage.onDidCloseStorage)(() => { this.logService.trace(`StorageMainService: closed workspace storage (${workspace.id})`); this.mapWorkspaceToStorage.delete(workspace.id); - }); + })); } return workspaceStorage; diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index 4ace3be8706..93ac0d93721 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -11,11 +11,14 @@ export function createSuite(params: { setup: () => Pr let storageService: T; + const disposables = new DisposableStore(); + setup(async () => { storageService = await params.setup(); }); teardown(() => { + disposables.clear(); return params.teardown(storageService); }); @@ -33,7 +36,7 @@ export function createSuite(params: { setup: () => Pr test('Storage change source', () => { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); // Explicit external source storageService.storeAll([{ key: 'testExternalChange', value: 'foobar', scope: StorageScope.WORKSPACE, target: StorageTarget.MACHINE }], true); @@ -52,7 +55,7 @@ export function createSuite(params: { setup: () => Pr test('Storage change event scope (all keys)', () => { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(StorageScope.WORKSPACE, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); storageService.store('testChange', 'foobar', StorageScope.WORKSPACE, StorageTarget.MACHINE); storageService.store('testChange2', 'foobar', StorageScope.WORKSPACE, StorageTarget.MACHINE); @@ -64,7 +67,7 @@ export function createSuite(params: { setup: () => Pr test('Storage change event scope (specific key)', () => { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(StorageScope.WORKSPACE, 'testChange', new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(StorageScope.WORKSPACE, 'testChange', disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); storageService.store('testChange', 'foobar', StorageScope.WORKSPACE, StorageTarget.MACHINE); storageService.store('testChange', 'foobar', StorageScope.PROFILE, StorageTarget.USER); @@ -77,7 +80,7 @@ export function createSuite(params: { setup: () => Pr function storeData(scope: StorageScope): void { let storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(scope, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(scope, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); strictEqual(storageService.get('test.get', scope, 'foobar'), 'foobar'); strictEqual(storageService.get('test.get', scope, ''), ''); @@ -153,7 +156,7 @@ export function createSuite(params: { setup: () => Pr function removeData(scope: StorageScope): void { const storageValueChangeEvents: IStorageValueChangeEvent[] = []; - storageService.onDidChangeValue(scope, undefined, new DisposableStore())(e => storageValueChangeEvents.push(e)); + storageService.onDidChangeValue(scope, undefined, disposables)(e => storageValueChangeEvents.push(e), undefined, disposables); storageService.store('test.remove', 'foobar', scope, StorageTarget.MACHINE); strictEqual('foobar', storageService.get('test.remove', scope, (undefined)!)); @@ -167,7 +170,7 @@ export function createSuite(params: { setup: () => Pr test('Keys (in-memory)', () => { let storageTargetEvent: IStorageTargetChangeEvent | undefined = undefined; - storageService.onDidChangeTarget(e => storageTargetEvent = e); + storageService.onDidChangeTarget(e => storageTargetEvent = e, undefined, disposables); // Empty for (const scope of [StorageScope.WORKSPACE, StorageScope.PROFILE, StorageScope.APPLICATION]) { @@ -180,7 +183,7 @@ export function createSuite(params: { setup: () => Pr // Add values for (const scope of [StorageScope.WORKSPACE, StorageScope.PROFILE, StorageScope.APPLICATION]) { - storageService.onDidChangeValue(scope, undefined, new DisposableStore())(e => storageValueChangeEvent = e); + storageService.onDidChangeValue(scope, undefined, disposables)(e => storageValueChangeEvent = e, undefined, disposables); for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { storageTargetEvent = Object.create(null); 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 3ca4ea12ffb..f3467af580f 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -24,9 +24,13 @@ import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentitySe import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; import { TestLifecycleMainService } from 'vs/platform/test/electron-main/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('StorageMainService', function () { + const disposables = new DisposableStore(); + const productService: IProductService = { _serviceBrand: undefined, ...product }; const inMemoryProfileRoot = URI.file('/location').with({ scheme: Schemas.inMemory }); @@ -68,12 +72,12 @@ suite('StorageMainService', function () { } let storageChangeEvent: IStorageChangeEvent | undefined = undefined; - const storageChangeListener = storage.onDidChangeStorage(e => { + disposables.add(storage.onDidChangeStorage(e => { storageChangeEvent = e; - }); + })); let storageDidClose = false; - const storageCloseListener = storage.onDidCloseStorage(() => storageDidClose = true); + disposables.add(storage.onDidCloseStorage(() => storageDidClose = true)); // Basic store/get/remove const size = storage.items.size; @@ -101,15 +105,21 @@ suite('StorageMainService', function () { await storage.close(); strictEqual(storageDidClose, true); - - storageChangeListener.dispose(); - storageCloseListener.dispose(); } + teardown(() => { + disposables.clear(); + }); + function createStorageService(lifecycleMainService: ILifecycleMainService = new TestLifecycleMainService()): TestStorageMainService { const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); - const fileService = new FileService(new NullLogService()); - return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesMainService(new StateService(SaveStrategy.DELAYED, environmentService, new NullLogService(), fileService), new UriIdentityService(fileService), environmentService, fileService, new NullLogService()), lifecycleMainService, fileService, new UriIdentityService(fileService)); + const fileService = disposables.add(new FileService(new NullLogService())); + const uriIdentityService = disposables.add(new UriIdentityService(fileService)); + const testStorageService = disposables.add(new TestStorageMainService(new NullLogService(), environmentService, disposables.add(new UserDataProfilesMainService(new StateService(SaveStrategy.DELAYED, environmentService, new NullLogService(), fileService), disposables.add(uriIdentityService), environmentService, fileService, new NullLogService())), lifecycleMainService, fileService, uriIdentityService)); + + disposables.add(testStorageService.applicationStorage); + + return testStorageService; } test('basics (application)', function () { @@ -141,21 +151,21 @@ suite('StorageMainService', function () { const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; - workspaceStorage.onDidCloseStorage(() => { + disposables.add(workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; - }); + })); const profileStorage = storageMainService.profileStorage(profile); let didCloseProfileStorage = false; - profileStorage.onDidCloseStorage(() => { + disposables.add(profileStorage.onDidCloseStorage(() => { didCloseProfileStorage = true; - }); + })); const applicationStorage = storageMainService.applicationStorage; let didCloseApplicationStorage = false; - applicationStorage.onDidCloseStorage(() => { + disposables.add(applicationStorage.onDidCloseStorage(() => { didCloseApplicationStorage = true; - }); + })); strictEqual(applicationStorage, storageMainService.applicationStorage); // same instance as long as not closed strictEqual(profileStorage, storageMainService.profileStorage(profile)); // same instance as long as not closed @@ -177,7 +187,7 @@ suite('StorageMainService', function () { const workspaceStorage2 = storageMainService.workspaceStorage(workspace); notStrictEqual(workspaceStorage, workspaceStorage2); - return workspaceStorage2.close(); + await workspaceStorage2.close(); }); test('storage closed before init works', async function () { @@ -187,21 +197,21 @@ suite('StorageMainService', function () { const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; - workspaceStorage.onDidCloseStorage(() => { + disposables.add(workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; - }); + })); const profileStorage = storageMainService.profileStorage(profile); let didCloseProfileStorage = false; - profileStorage.onDidCloseStorage(() => { + disposables.add(profileStorage.onDidCloseStorage(() => { didCloseProfileStorage = true; - }); + })); const applicationStorage = storageMainService.applicationStorage; let didCloseApplicationStorage = false; - applicationStorage.onDidCloseStorage(() => { + disposables.add(applicationStorage.onDidCloseStorage(() => { didCloseApplicationStorage = true; - }); + })); await applicationStorage.close(); await profileStorage.close(); @@ -219,21 +229,21 @@ suite('StorageMainService', function () { const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; - workspaceStorage.onDidCloseStorage(() => { + disposables.add(workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; - }); + })); const profileStorage = storageMainService.profileStorage(profile); let didCloseProfileStorage = false; - profileStorage.onDidCloseStorage(() => { + disposables.add(profileStorage.onDidCloseStorage(() => { didCloseProfileStorage = true; - }); + })); const applicationtorage = storageMainService.applicationStorage; let didCloseApplicationStorage = false; - applicationtorage.onDidCloseStorage(() => { + disposables.add(applicationtorage.onDidCloseStorage(() => { didCloseApplicationStorage = true; - }); + })); applicationtorage.init(); profileStorage.init(); @@ -247,4 +257,6 @@ suite('StorageMainService', function () { strictEqual(didCloseProfileStorage, true); strictEqual(didCloseWorkspaceStorage, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts index 1de67c853f8..5fed6253090 100644 --- a/src/vs/workbench/browser/parts/editor/editorAutoSave.ts +++ b/src/vs/workbench/browser/parts/editor/editorAutoSave.ts @@ -191,7 +191,7 @@ export class EditorAutoSave extends Disposable implements IWorkbenchContribution const handle = setTimeout(() => { // Clear disposable - this.pendingAutoSavesAfterDelay.delete(workingCopy); + this.discardAutoSave(workingCopy); // Save if dirty if (workingCopy.isDirty()) { diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 5d6b14d633a..ac3fced8d68 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -808,13 +808,13 @@ export class TabsTitleControl extends TitleControl { const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index }); const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner }); - tabActionBar.onWillRun(e => { + const tabActionListener = tabActionBar.onWillRun(e => { if (e.action.id === this.closeEditorAction.id) { this.blockRevealActiveTabOnce(); } }); - const tabActionBarDisposable = combinedDisposable(tabActionBar, toDisposable(insert(this.tabActionBars, tabActionBar))); + const tabActionBarDisposable = combinedDisposable(tabActionBar, tabActionListener, toDisposable(insert(this.tabActionBars, tabActionBar))); // Tab Border Bottom const tabBorderBottomContainer = document.createElement('div'); diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts index 6a1743a35f9..36a58fb81ba 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsActions.test.ts @@ -25,7 +25,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IExtensionService, toExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestWorkspaceTrustManagementService } from 'vs/workbench/test/common/workbenchTestServices'; import { TestExtensionTipsService, TestSharedProcessService } from 'vs/workbench/test/electron-sandbox/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; @@ -52,7 +52,6 @@ import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/u import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { platform } from 'vs/base/common/platform'; import { arch } from 'vs/base/common/process'; @@ -140,7 +139,7 @@ function setupTest() { instantiationService.stub(IUserDataSyncEnablementService, instantiationService.createInstance(UserDataSyncEnablementService)); instantiationService.set(IExtensionsWorkbenchService, disposables.add(instantiationService.createInstance(ExtensionsWorkbenchService))); - instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService()); + instantiationService.stub(IWorkspaceTrustManagementService, disposables.add(new TestWorkspaceTrustManagementService())); } diff --git a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts index 87691ff1fa3..1f89d3dc33c 100644 --- a/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/editorAutoSave.test.ts @@ -5,10 +5,10 @@ import * as assert from 'assert'; import { Event } from 'vs/base/common/event'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { TestFilesConfigurationService, workbenchInstantiationService, TestServiceAccessor, registerTestFileEditor, createEditorPart, TestEnvironmentService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { IResolvedTextFileEditorModel, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; @@ -43,19 +43,19 @@ suite('EditorAutoSave', () => { configurationService.setUserConfiguration('files', autoSaveConfig); instantiationService.stub(IConfigurationService, configurationService); - instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService, new TestContextService(TestWorkspace), TestEnvironmentService, - new UriIdentityService(new TestFileService()), - new TestFileService() - )); + disposables.add(new UriIdentityService(disposables.add(new TestFileService()))), + disposables.add(new TestFileService()) + ))); const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); const accessor = instantiationService.createInstance(TestServiceAccessor); @@ -71,14 +71,14 @@ suite('EditorAutoSave', () => { const resource = toResource.call(this, '/path/index.txt'); - const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; - model.textEditorModel.setValue('Super Good'); + const model: ITextFileEditorModel = disposables.add(await accessor.textFileService.files.resolve(resource)); + model.textEditorModel?.setValue('Super Good'); assert.ok(model.isDirty()); await awaitModelSaved(model); - assert.ok(!model.isDirty()); + assert.strictEqual(model.isDirty(), false); }); test('editor auto saves on focus change if configured', async function () { @@ -87,19 +87,23 @@ suite('EditorAutoSave', () => { const resource = toResource.call(this, '/path/index.txt'); await accessor.editorService.openEditor({ resource, options: { override: DEFAULT_EDITOR_ASSOCIATION.id } }); - const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; - model.textEditorModel.setValue('Super Good'); + const model: ITextFileEditorModel = disposables.add(await accessor.textFileService.files.resolve(resource)); + model.textEditorModel?.setValue('Super Good'); assert.ok(model.isDirty()); - await accessor.editorService.openEditor({ resource: toResource.call(this, '/path/index_other.txt') }); + const editorPane = await accessor.editorService.openEditor({ resource: toResource.call(this, '/path/index_other.txt') }); await awaitModelSaved(model); - assert.ok(!model.isDirty()); + assert.strictEqual(model.isDirty(), false); + + await editorPane?.group?.closeAllEditors(); }); function awaitModelSaved(model: ITextFileEditorModel): Promise { return Event.toPromise(Event.once(model.onDidChangeDirty)); } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index d66d44e14bb..c9094af17fb 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -25,7 +25,7 @@ import { TextEditorService } from 'vs/workbench/services/textfile/common/textEdi suite('Files - FileEditorInput', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -44,7 +44,6 @@ suite('Files - FileEditorInput', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService({ textEditorService: instantiationService => instantiationService.createInstance(TestTextEditorService) }, disposables); @@ -53,7 +52,7 @@ suite('Files - FileEditorInput', () => { }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('Basics', async function () { diff --git a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts index b0afe7cc554..ff9eb5b4383 100644 --- a/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/textFileEditorTracker.test.ts @@ -6,9 +6,9 @@ import * as assert from 'assert'; import { Event } from 'vs/base/common/event'; import { TextFileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/textFileEditorTracker'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { workbenchInstantiationService, TestServiceAccessor, TestFilesConfigurationService, registerTestFileEditor, registerTestResourceEditor, createEditorPart, TestEnvironmentService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, TestFilesConfigurationService, registerTestFileEditor, registerTestResourceEditor, createEditorPart, TestEnvironmentService, TestFileService, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { IResolvedTextFileEditorModel, snapshotToString, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { FileChangesEvent, FileChangeType, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -25,8 +25,6 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { FILE_EDITOR_INPUT_ID } from 'vs/workbench/contrib/files/common/files'; -import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; @@ -52,7 +50,7 @@ suite('Files - TextFileEditorTracker', () => { disposables.clear(); }); - async function createTracker(autoSaveEnabled = false): Promise { + async function createTracker(autoSaveEnabled = false): Promise<{ accessor: TestServiceAccessor; cleanup: () => Promise }> { const instantiationService = workbenchInstantiationService(undefined, disposables); if (autoSaveEnabled) { @@ -61,22 +59,22 @@ suite('Files - TextFileEditorTracker', () => { instantiationService.stub(IConfigurationService, configurationService); - instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + const fileService = disposables.add(new TestFileService()); + + instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService, new TestContextService(TestWorkspace), TestEnvironmentService, - new UriIdentityService(new TestFileService()), - new TestFileService() - )); + disposables.add(new UriIdentityService(fileService)), + fileService + ))); } const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); disposables.add(editorService); instantiationService.stub(IEditorService, editorService); @@ -85,11 +83,16 @@ suite('Files - TextFileEditorTracker', () => { disposables.add(instantiationService.createInstance(TestTextFileEditorTracker)); - return accessor; + const cleanup = async () => { + await workbenchTeardown(instantiationService); + part.dispose(); + }; + + return { accessor, cleanup }; } test('file change event updates model', async function () { - const accessor = await createTracker(); + const { accessor, cleanup } = await createTracker(); const resource = toResource.call(this, '/path/index.txt'); @@ -107,6 +110,8 @@ suite('Files - TextFileEditorTracker', () => { await timeout(0); // due to event updating model async assert.strictEqual(snapshotToString(model.createSnapshot()!), 'Hello Html'); + + await cleanup(); }); test('dirty text file model opens as editor', async function () { @@ -134,7 +139,7 @@ suite('Files - TextFileEditorTracker', () => { }); async function testDirtyTextFileModelOpensEditorDependingOnAutoSaveSetting(resource: URI, autoSave: boolean, error: boolean): Promise { - const accessor = await createTracker(autoSave); + const { accessor, cleanup } = await createTracker(autoSave); assert.ok(!accessor.editorService.isOpened({ resource, typeId: FILE_EDITOR_INPUT_ID, editorId: DEFAULT_EDITOR_ASSOCIATION.id })); @@ -159,6 +164,8 @@ suite('Files - TextFileEditorTracker', () => { await awaitEditorOpening(accessor.editorService); assert.ok(accessor.editorService.isOpened({ resource, typeId: FILE_EDITOR_INPUT_ID, editorId: DEFAULT_EDITOR_ASSOCIATION.id })); } + + await cleanup(); } test('dirty untitled text file model opens as editor', function () { @@ -170,7 +177,7 @@ suite('Files - TextFileEditorTracker', () => { }); async function testUntitledEditor(autoSaveEnabled: boolean): Promise { - const accessor = await createTracker(autoSaveEnabled); + const { accessor, cleanup } = await createTracker(autoSaveEnabled); const untitledTextEditor = await accessor.textEditorService.resolveTextEditor({ resource: undefined, forceUntitled: true }) as UntitledTextEditorInput; const model = disposables.add(await untitledTextEditor.resolve()); @@ -181,6 +188,8 @@ suite('Files - TextFileEditorTracker', () => { await awaitEditorOpening(accessor.editorService); assert.ok(accessor.editorService.isOpened(untitledTextEditor)); + + await cleanup(); } function awaitEditorOpening(editorService: IEditorService): Promise { @@ -188,7 +197,7 @@ suite('Files - TextFileEditorTracker', () => { } test('non-dirty files reload on window focus', async function () { - const accessor = await createTracker(); + const { accessor, cleanup } = await createTracker(); const resource = toResource.call(this, '/path/index.txt'); @@ -198,6 +207,8 @@ suite('Files - TextFileEditorTracker', () => { accessor.hostService.setFocus(true); await awaitModelResolveEvent(accessor.textFileService, resource); + + await cleanup(); }); function awaitModelResolveEvent(textFileService: ITextFileService, resource: URI): Promise { @@ -210,4 +221,6 @@ suite('Files - TextFileEditorTracker', () => { }); }); } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index c7bcfcf6d3b..fb7f0a56f29 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -58,9 +58,8 @@ import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookO import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { TextModelResolverService } from 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import { IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices'; -import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestStorageService, TestWorkspaceTrustRequestService } from 'vs/workbench/test/common/workbenchTestServices'; import { FontInfo } from 'vs/editor/common/config/fontInfo'; import { EditorFontLigatures, EditorFontVariations } from 'vs/editor/common/config/editorOptions'; @@ -189,7 +188,7 @@ export function setupInstantiationService(disposables = new DisposableStore()) { instantiationService.stub(ILogService, new NullLogService()); instantiationService.stub(IClipboardService, TestClipboardService); instantiationService.stub(IStorageService, new TestStorageService()); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(true)); + instantiationService.stub(IWorkspaceTrustRequestService, disposables.add(new TestWorkspaceTrustRequestService(true))); instantiationService.stub(INotebookExecutionStateService, new TestNotebookExecutionStateService()); instantiationService.stub(IKeybindingService, new MockKeybindingService()); instantiationService.stub(INotebookCellStatusBarService, new NotebookCellStatusBarService()); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index b0ad29a3597..5b3a558fba2 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -431,7 +431,7 @@ export class TestingExplorerView extends ViewPane { this.dimensions.height = height; this.dimensions.width = width; this.container.style.height = `${height}px`; - this.viewModel.layout(height - this.treeHeader.clientHeight, width); + this.viewModel?.layout(height - this.treeHeader.clientHeight, width); this.filter.value?.layout(width); } } diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index ae5ee65a0b7..f67808e1483 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -8,7 +8,7 @@ import { EditorActivation, IResourceEditorInput } from 'vs/platform/editor/commo import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { DEFAULT_EDITOR_ASSOCIATION, EditorCloseContext, EditorsOrder, IEditorCloseEvent, EditorInputWithOptions, IEditorPane, IResourceDiffEditorInput, isEditorInputWithOptions, IUntitledTextResourceEditorInput, IUntypedEditorInput, SideBySideEditor, isEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; -import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput, ITestInstantiationService, registerTestResourceEditor, registerTestSideBySideEditor, createEditorPart, registerTestFileEditor, TestTextFileEditor, TestSingletonFileEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, TestFileEditorInput, ITestInstantiationService, registerTestResourceEditor, registerTestSideBySideEditor, createEditorPart, registerTestFileEditor, TestTextFileEditor, TestSingletonFileEditorInput, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorGroup, IEditorGroupsService, GroupDirection, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; @@ -20,8 +20,7 @@ import { FileOperationEvent, FileOperation } from 'vs/platform/files/common/file import { DisposableStore } from 'vs/base/common/lifecycle'; import { MockScopableContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; -import { IWorkspaceTrustRequestService, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; +import { WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { ErrorPlaceholderEditor } from 'vs/workbench/browser/parts/editor/editorPlaceholder'; @@ -50,9 +49,7 @@ suite('EditorService', () => { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); return [part, editorService, instantiationService.createInstance(TestServiceAccessor)]; @@ -589,13 +586,7 @@ suite('EditorService', () => { lastUntitledEditorFactoryEditor = undefined; lastDiffEditorFactoryEditor = undefined; - for (const group of part.groups) { - await group.closeAllEditors(); - } - - for (const group of part.groups) { - accessor.editorGroupService.removeGroup(group); - } + await workbenchTeardown(accessor.instantiationService); rootGroup = part.activeGroup; } diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 2faa51fa86b..bbfd1d8437a 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -30,12 +30,12 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { mock } from 'vs/base/test/common/mock'; import { IExtensionBisectService } from 'vs/workbench/services/extensionManagement/browser/extensionBisect'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, WorkspaceTrustRequestOptions } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustEnablementService, TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { ExtensionManifestPropertiesService, IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; -import { TestContextService, TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestProductService, TestWorkspaceTrustEnablementService, TestWorkspaceTrustManagementService } from 'vs/workbench/test/common/workbenchTestServices'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { ExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagementService'; import { NullLogService } from 'vs/platform/log/common/log'; +import { DisposableStore } from 'vs/base/common/lifecycle'; function createStorageService(instantiationService: TestInstantiationService): IStorageService { let service = instantiationService.get(IStorageService); @@ -70,7 +70,8 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { }, null, null)); const extensionManagementService = instantiationService.createInstance(ExtensionManagementService); const workbenchExtensionManagementService = instantiationService.get(IWorkbenchExtensionManagementService) || instantiationService.stub(IWorkbenchExtensionManagementService, extensionManagementService); - const workspaceTrustManagementService = instantiationService.get(IWorkspaceTrustManagementService) || instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService()); + const disposables = new DisposableStore(); + const workspaceTrustManagementService = instantiationService.get(IWorkspaceTrustManagementService) || instantiationService.stub(IWorkspaceTrustManagementService, disposables.add(new TestWorkspaceTrustManagementService())); super( storageService, new GlobalExtensionEnablementService(storageService, extensionManagementService), @@ -90,6 +91,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.get(IExtensionManifestPropertiesService) || instantiationService.stub(IExtensionManifestPropertiesService, new ExtensionManifestPropertiesService(TestProductService, new TestConfigurationService(), new TestWorkspaceTrustEnablementService(), new NullLogService())), instantiationService ); + this._register(disposables); } public async waitUntilInitialized(): Promise { diff --git a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts index 07e5b50f154..1924d01fe44 100644 --- a/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts +++ b/src/vs/workbench/services/extensions/test/common/extensionManifestPropertiesService.test.ts @@ -7,12 +7,11 @@ import * as assert from 'assert'; import { IExtensionManifest, ExtensionUntrustedWorkspaceSupportType } from 'vs/platform/extensions/common/extensions'; import { ExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestProductService, TestWorkspaceTrustEnablementService } from 'vs/workbench/test/common/workbenchTestServices'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProductService } from 'vs/platform/product/common/productService'; import { isWeb } from 'vs/base/common/platform'; -import { TestWorkspaceTrustEnablementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IWorkspaceTrustEnablementService } from 'vs/platform/workspace/common/workspaceTrust'; import { NullLogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/services/history/test/browser/historyService.test.ts b/src/vs/workbench/services/history/test/browser/historyService.test.ts index db24e25d864..659a066aacf 100644 --- a/src/vs/workbench/services/history/test/browser/historyService.test.ts +++ b/src/vs/workbench/services/history/test/browser/historyService.test.ts @@ -40,7 +40,7 @@ suite('HistoryService', function () { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); const configurationService = new TestConfigurationService(); diff --git a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts index 34604db9ea4..8561266eae5 100644 --- a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts +++ b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts @@ -12,7 +12,7 @@ import { workbenchInstantiationService } from 'vs/workbench/test/electron-sandbo suite('Lifecycleservice', function () { let lifecycleService: TestLifecycleService; - let disposables: DisposableStore; + const disposables = new DisposableStore(); class TestLifecycleService extends NativeLifecycleService { @@ -26,14 +26,12 @@ suite('Lifecycleservice', function () { } setup(async () => { - disposables = new DisposableStore(); - const instantiationService = workbenchInstantiationService(undefined, disposables); lifecycleService = instantiationService.createInstance(TestLifecycleService); }); teardown(async () => { - disposables.dispose(); + disposables.clear(); }); test('onBeforeShutdown - final veto called after other vetos', async function () { diff --git a/src/vs/workbench/services/storage/test/browser/storageService.test.ts b/src/vs/workbench/services/storage/test/browser/storageService.test.ts index 6653a3efbd1..25472109405 100644 --- a/src/vs/workbench/services/storage/test/browser/storageService.test.ts +++ b/src/vs/workbench/services/storage/test/browser/storageService.test.ts @@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { IStorageChangeEvent, Storage } from 'vs/base/parts/storage/common/storage'; import { flakySuite } from 'vs/base/test/common/testUtils'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { FileService } from 'vs/platform/files/common/fileService'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -49,7 +50,7 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS cacheHome: joinPath(inMemoryExtraProfileRoot, 'cache') }; - const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, new UserDataProfileService(inMemoryExtraProfile, new UserDataProfilesService(TestEnvironmentService, fileService, new UriIdentityService(fileService), logService)), logService)); + const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, disposables.add(new UserDataProfileService(inMemoryExtraProfile, new UserDataProfilesService(TestEnvironmentService, fileService, disposables.add(new UriIdentityService(fileService)), logService))), logService)); await storageService.initialize(); @@ -73,6 +74,8 @@ flakySuite('StorageService (browser)', function () { disposables.clear(); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); flakySuite('StorageService (browser specific)', () => { @@ -110,6 +113,8 @@ flakySuite('StorageService (browser specific)', () => { } }); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); flakySuite('IndexDBStorageDatabase (browser)', () => { @@ -117,13 +122,17 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { const id = 'workspace-storage-db-test'; const logService = new NullLogService(); + const disposables = new DisposableStore(); + teardown(async () => { - const storage = await IndexedDBStorageDatabase.create({ id }, logService); + const storage = disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)); await storage.clear(); + + disposables.clear(); }); test('Basics', async () => { - let storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + let storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -145,7 +154,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -168,7 +177,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -196,7 +205,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -209,7 +218,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { }); test('Clear', async () => { - let storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + let storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -219,13 +228,13 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - const db = await IndexedDBStorageDatabase.create({ id }, logService); - storage = new Storage(db); + const db = disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(db)); await storage.init(); await db.clear(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -238,7 +247,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { }); test('Inserts and Deletes at the same time', async () => { - let storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + let storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -248,7 +257,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -260,7 +269,7 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { await storage.close(); - storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); await storage.init(); @@ -271,9 +280,9 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { }); test('Storage change event', async () => { - const storage = new Storage(await IndexedDBStorageDatabase.create({ id }, logService)); + const storage = disposables.add(new Storage(disposables.add(await IndexedDBStorageDatabase.create({ id }, logService)))); let storageChangeEvents: IStorageChangeEvent[] = []; - storage.onDidChangeStorage(e => storageChangeEvents.push(e)); + disposables.add(storage.onDidChangeStorage(e => storageChangeEvents.push(e))); await storage.init(); @@ -294,4 +303,6 @@ flakySuite('IndexDBStorageDatabase (browser)', () => { storageValueChangeEvent = storageChangeEvents.find(e => e.key === 'isExternal'); strictEqual(storageValueChangeEvent?.external, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts b/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts index 58d93d4b786..4ae038ffe92 100644 --- a/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/browserTextFileService.io.test.ts @@ -22,6 +22,7 @@ import { isWeb } from 'vs/base/common/platform'; import { IWorkingCopyFileService, WorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService'; import { WorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; // optimization: we don't need to run this suite in native environment, // because we have nativeTextFileService.io.test.ts for it, @@ -39,18 +40,17 @@ if (isWeb) { const instantiationService = workbenchInstantiationService(undefined, disposables); const logService = new NullLogService(); - const fileService = new FileService(logService); + const fileService = disposables.add(new FileService(logService)); - fileProvider = new TestInMemoryFileSystemProvider(); + fileProvider = disposables.add(new TestInMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); + collection.set(IWorkingCopyFileService, disposables.add(new WorkingCopyFileService(fileService, disposables.add(new WorkingCopyService()), instantiationService, disposables.add(new UriIdentityService(fileService))))); - collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new WorkingCopyService(), instantiationService, new UriIdentityService(fileService))); - - service = instantiationService.createChild(collection).createInstance(TestBrowserTextFileServiceWithEncodingOverrides); + service = disposables.add(instantiationService.createChild(collection).createInstance(TestBrowserTextFileServiceWithEncodingOverrides)); + disposables.add(service.files); await fileProvider.mkdir(URI.file(testDir)); for (const fileName in files) { @@ -65,8 +65,6 @@ if (isWeb) { }, teardown: async () => { - (service.files).dispose(); - disposables.clear(); }, @@ -111,5 +109,7 @@ if (isWeb) { return null; // ignore errors (like file not found) } } + + ensureNoDisposablesAreLeakedInTestSuite(); }); } diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index 7ca2a09bfb1..c0b6d62e26e 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -28,22 +28,21 @@ suite('Files - TextFileEditorModel', () => { return stat ? stat.mtime : -1; } - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let content: string; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); content = accessor.fileService.getContent(); + disposables.add(accessor.textFileService.files); }); teardown(() => { - (accessor.textFileService.files).dispose(); accessor.fileService.setContent(content); - disposables.dispose(); + disposables.clear(); }); test('basic events', async function () { diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts index 3a46ef121f9..71b9b4c9957 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModelManager.test.ts @@ -18,18 +18,17 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Files - TextFileEditorModelManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('add, remove, clear, get, getAll', function () { diff --git a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts index 45bd9ba71fd..a16c6693c1e 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts @@ -13,21 +13,20 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Files - TextFileService', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let model: TextFileEditorModel; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.textFileService.files); }); teardown(() => { model?.dispose(); - (accessor.textFileService.files).dispose(); - disposables.dispose(); + disposables.clear(); }); test('isDirty/getDirty - files and untitled', async function () { diff --git a/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts index 3bc8637e4aa..02f4f1e4bc2 100644 --- a/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/common/textFileService.io.test.ts @@ -13,6 +13,7 @@ import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { ITextSnapshot, DefaultEndOfLine } from 'vs/editor/common/model'; import { isWindows } from 'vs/base/common/platform'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export interface Params { setup(): Promise<{ @@ -40,6 +41,7 @@ export default function createSuite(params: Params) { let service: ITextFileService; let testDir = ''; const { exists, stat, readFile, detectEncodingByBOM } = params; + const disposables = new DisposableStore(); setup(async () => { const result = await params.setup(); @@ -49,6 +51,7 @@ export default function createSuite(params: Params) { teardown(async () => { await params.teardown(); + disposables.clear(); }); test('create - no encoding - content empty', async () => { @@ -165,9 +168,9 @@ export default function createSuite(params: Params) { }); function createTextModelSnapshot(text: string, preserveBOM?: boolean): ITextSnapshot { - const textModel = createTextModel(text); + const textModel = disposables.add(createTextModel(text)); const snapshot = textModel.createSnapshot(preserveBOM); - textModel.dispose(); + return snapshot; } @@ -224,7 +227,8 @@ export default function createSuite(params: Params) { const resolved = await service.readStream(resource); assert.strictEqual(resolved.encoding, encoding); - assert.strictEqual(snapshotToString(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer.createSnapshot(false)), expectedContent); + const textBuffer = disposables.add(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer); + assert.strictEqual(snapshotToString(textBuffer.createSnapshot(false)), expectedContent); } test('write - use encoding (cp1252)', async () => { @@ -252,18 +256,21 @@ export default function createSuite(params: Params) { async function testEncodingKeepsData(resource: URI, encoding: string, expected: string) { let resolved = await service.readStream(resource, { encoding }); - const content = snapshotToString(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer.createSnapshot(false)); + const textBuffer = disposables.add(resolved.value.create(isWindows ? DefaultEndOfLine.CRLF : DefaultEndOfLine.LF).textBuffer); + const content = snapshotToString(textBuffer.createSnapshot(false)); assert.strictEqual(content, expected); await service.write(resource, content, { encoding }); resolved = await service.readStream(resource, { encoding }); - assert.strictEqual(snapshotToString(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer.createSnapshot(false)), content); + const textBuffer2 = disposables.add(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer); + assert.strictEqual(snapshotToString(textBuffer2.createSnapshot(false)), content); await service.write(resource, createTextModelSnapshot(content), { encoding }); resolved = await service.readStream(resource, { encoding }); - assert.strictEqual(snapshotToString(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer.createSnapshot(false)), content); + const textBuffer3 = disposables.add(resolved.value.create(DefaultEndOfLine.CRLF).textBuffer); + assert.strictEqual(snapshotToString(textBuffer3.createSnapshot(false)), content); } test('write - no encoding - content as string', async () => { @@ -340,7 +347,7 @@ export default function createSuite(params: Params) { let detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.strictEqual(detectedEncoding, null); - const model = createTextModel((await readFile(resource.fsPath)).toString() + 'updates'); + const model = disposables.add(createTextModel((await readFile(resource.fsPath)).toString() + 'updates')); await service.write(resource, model.createSnapshot(), { encoding: UTF8_with_bom }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); @@ -360,8 +367,6 @@ export default function createSuite(params: Params) { await service.write(resource, model.createSnapshot(), { encoding: UTF8 }); detectedEncoding = await detectEncodingByBOM(resource.fsPath); assert.strictEqual(detectedEncoding, null); - - model.dispose(); }); test('write - preserve UTF8 BOM - content as string', async () => { @@ -412,8 +417,9 @@ export default function createSuite(params: Params) { assert.strictEqual(result.size, (await stat(resource.fsPath)).size); const content = (await readFile(resource.fsPath)).toString(); + const textBuffer = disposables.add(result.value.create(DefaultEndOfLine.LF).textBuffer); assert.strictEqual( - snapshotToString(result.value.create(DefaultEndOfLine.LF).textBuffer.createSnapshot(false)), + snapshotToString(textBuffer.createSnapshot(false)), snapshotToString(createTextModelSnapshot(content, false))); } @@ -541,7 +547,8 @@ export default function createSuite(params: Params) { const result = await service.readStream(resource, { encoding }); assert.strictEqual(result.encoding, encoding); - let contents = snapshotToString(result.value.create(DefaultEndOfLine.LF).textBuffer.createSnapshot(false)); + const textBuffer = disposables.add(result.value.create(DefaultEndOfLine.LF).textBuffer); + let contents = snapshotToString(textBuffer.createSnapshot(false)); assert.strictEqual(contents.indexOf(needle), 0); assert.ok(contents.indexOf(needle, 10) > 0); @@ -557,7 +564,8 @@ export default function createSuite(params: Params) { const factory = await createTextBufferFactoryFromStream(await service.getDecodedStream(resource, bufferToStream(rawFileVSBuffer), { encoding })); - contents = snapshotToString(factory.create(DefaultEndOfLine.LF).textBuffer.createSnapshot(false)); + const textBuffer2 = disposables.add(factory.create(DefaultEndOfLine.LF).textBuffer); + contents = snapshotToString(textBuffer2.createSnapshot(false)); assert.strictEqual(contents.indexOf(needle), 0); assert.ok(contents.indexOf(needle, 10) > 0); diff --git a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts index 707780e4c2a..94864bb3094 100644 --- a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts @@ -35,18 +35,17 @@ suite('Files - NativeTextFileService i/o', function () { const instantiationService = workbenchInstantiationService(undefined, disposables); const logService = new NullLogService(); - const fileService = new FileService(logService); + const fileService = disposables.add(new FileService(logService)); - fileProvider = new TestInMemoryFileSystemProvider(); + fileProvider = disposables.add(new TestInMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); + collection.set(IWorkingCopyFileService, disposables.add(new WorkingCopyFileService(fileService, disposables.add(new WorkingCopyService()), instantiationService, disposables.add(new UriIdentityService(fileService))))); - collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new WorkingCopyService(), instantiationService, new UriIdentityService(fileService))); - - service = instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides); + service = disposables.add(instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides)); + disposables.add(service.files); await fileProvider.mkdir(URI.file(testDir)); for (const fileName in files) { @@ -61,8 +60,6 @@ suite('Files - NativeTextFileService i/o', function () { }, teardown: async () => { - (service.files).dispose(); - disposables.clear(); }, diff --git a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts index 1ac327fd20c..a48728e4a7a 100644 --- a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.test.ts @@ -19,7 +19,7 @@ import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentitySe import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; suite('Files - NativeTextFileService', function () { const disposables = new DisposableStore(); @@ -31,28 +31,25 @@ suite('Files - NativeTextFileService', function () { instantiationService = workbenchInstantiationService(undefined, disposables); const logService = new NullLogService(); - const fileService = new FileService(logService); + const fileService = disposables.add(new FileService(logService)); - const fileProvider = new InMemoryFileSystemProvider(); + const fileProvider = disposables.add(new InMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.file, fileProvider)); - disposables.add(fileProvider); const collection = new ServiceCollection(); collection.set(IFileService, fileService); + collection.set(IWorkingCopyFileService, disposables.add(new WorkingCopyFileService(fileService, disposables.add(new WorkingCopyService()), instantiationService, disposables.add(new UriIdentityService(fileService))))); - collection.set(IWorkingCopyFileService, new WorkingCopyFileService(fileService, new WorkingCopyService(), instantiationService, new UriIdentityService(fileService))); - - service = instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides); + service = disposables.add(instantiationService.createChild(collection).createInstance(TestNativeTextFileServiceWithEncodingOverrides)); + disposables.add(service.files); }); teardown(() => { - (service.files).dispose(); - disposables.clear(); }); test('shutdown joins on pending saves', async function () { - const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined)); await model.resolve(); @@ -67,4 +64,6 @@ suite('Files - NativeTextFileService', function () { assert.strictEqual(pendingSaveAwaited, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts index 9d7af92be98..4ccf202e643 100644 --- a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts @@ -22,21 +22,18 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Workbench - TextModelResolverService', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; - let model: TextFileEditorModel; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.textFileService.files); }); teardown(() => { - model?.dispose(); - (accessor.textFileService.files).dispose(); - disposables.dispose(); + disposables.clear(); }); test('resolve resource', async () => { diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 935b78a3dfd..8a0c6dc8529 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -24,19 +24,18 @@ import { LanguageDetectionLanguageEventSource } from 'vs/workbench/services/lang suite('Untitled text editors', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.untitledTextEditorService as UntitledTextEditorService); }); teardown(() => { - (accessor.untitledTextEditorService as UntitledTextEditorService).dispose(); - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts index f9ec56ad11a..16715e8b21d 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyBackupService.ts @@ -116,7 +116,7 @@ export class WorkingCopyBackupsModel { } } -export abstract class WorkingCopyBackupService implements IWorkingCopyBackupService { +export abstract class WorkingCopyBackupService extends Disposable implements IWorkingCopyBackupService { declare readonly _serviceBrand: undefined; @@ -127,7 +127,9 @@ export abstract class WorkingCopyBackupService implements IWorkingCopyBackupServ @IFileService protected fileService: IFileService, @ILogService private readonly logService: ILogService ) { - this.impl = this.initialize(backupWorkspaceHome); + super(); + + this.impl = this._register(this.initialize(backupWorkspaceHome)); } private initialize(backupWorkspaceHome: URI | undefined): WorkingCopyBackupServiceImpl | InMemoryWorkingCopyBackupService { @@ -533,13 +535,15 @@ class WorkingCopyBackupServiceImpl extends Disposable implements IWorkingCopyBac } } -export class InMemoryWorkingCopyBackupService implements IWorkingCopyBackupService { +export class InMemoryWorkingCopyBackupService extends Disposable implements IWorkingCopyBackupService { declare readonly _serviceBrand: undefined; private backups = new ResourceMap<{ typeId: string; content: VSBuffer; meta?: IWorkingCopyBackupMeta }>(); - constructor() { } + constructor() { + super(); + } async hasBackups(): Promise { return this.backups.size > 0; diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts b/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts index 8775ac71554..8e0f773f30c 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyBackupTracker.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; -import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IWorkingCopy, IWorkingCopyIdentifier, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { ILogService } from 'vs/platform/log/common/log'; @@ -56,8 +56,8 @@ export abstract class WorkingCopyBackupTracker extends Disposable { this._register(this.workingCopyService.onDidChangeContent(workingCopy => this.onDidChangeContent(workingCopy))); // Lifecycle - this.lifecycleService.onBeforeShutdown(event => (event as InternalBeforeShutdownEvent).finalVeto(() => this.onFinalBeforeShutdown(event.reason), 'veto.backups')); - this.lifecycleService.onWillShutdown(() => this.onWillShutdown()); + this._register(this.lifecycleService.onBeforeShutdown(event => (event as InternalBeforeShutdownEvent).finalVeto(() => this.onFinalBeforeShutdown(event.reason), 'veto.backups'))); + this._register(this.lifecycleService.onWillShutdown(() => this.onWillShutdown())); // Once a handler registers, restore backups this._register(this.workingCopyEditorService.onDidRegisterHandler(handler => this.restoreBackups(handler))); @@ -103,8 +103,8 @@ export abstract class WorkingCopyBackupTracker extends Disposable { // A map of scheduled pending backup operations for working copies // Given https://github.com/microsoft/vscode/issues/158038, we explicitly // do not store `IWorkingCopy` but the identifier in the map, since it - // looks like GC is not runnin for the working copy otherwise. - protected readonly pendingBackupOperations = new Map(); + // looks like GC is not running for the working copy otherwise. + protected readonly pendingBackupOperations = new Map void }>(); private suspended = false; @@ -206,17 +206,22 @@ export abstract class WorkingCopyBackupTracker extends Disposable { // Clear disposable unless we got canceled which would // indicate another operation has started meanwhile if (!cts.token.isCancellationRequested) { - this.pendingBackupOperations.delete(workingCopyIdentifier); + this.doClearPendingBackupOperation(workingCopyIdentifier); } }, this.getBackupScheduleDelay(workingCopy)); // Keep in map for disposal as needed - this.pendingBackupOperations.set(workingCopyIdentifier, toDisposable(() => { - this.logService.trace(`[backup tracker] clearing pending backup creation`, workingCopy.resource.toString(), workingCopy.typeId); + this.pendingBackupOperations.set(workingCopyIdentifier, { + cancel: () => { + this.logService.trace(`[backup tracker] clearing pending backup creation`, workingCopy.resource.toString(), workingCopy.typeId); - cts.dispose(true); - clearTimeout(handle); - })); + cts.cancel(); + }, + disposable: toDisposable(() => { + cts.dispose(); + clearTimeout(handle); + }) + }); } protected getBackupScheduleDelay(workingCopy: IWorkingCopy): number { @@ -247,11 +252,14 @@ export abstract class WorkingCopyBackupTracker extends Disposable { this.doDiscardBackup(workingCopyIdentifier, cts); // Keep in map for disposal as needed - this.pendingBackupOperations.set(workingCopyIdentifier, toDisposable(() => { - this.logService.trace(`[backup tracker] clearing pending backup discard`, workingCopy.resource.toString(), workingCopy.typeId); + this.pendingBackupOperations.set(workingCopyIdentifier, { + cancel: () => { + this.logService.trace(`[backup tracker] clearing pending backup discard`, workingCopy.resource.toString(), workingCopy.typeId); - cts.dispose(true); - })); + cts.cancel(); + }, + disposable: cts + }); } private async doDiscardBackup(workingCopyIdentifier: IWorkingCopyIdentifier, cts: CancellationTokenSource) { @@ -267,7 +275,7 @@ export abstract class WorkingCopyBackupTracker extends Disposable { // Clear disposable unless we got canceled which would // indicate another operation has started meanwhile if (!cts.token.isCancellationRequested) { - this.pendingBackupOperations.delete(workingCopyIdentifier); + this.doClearPendingBackupOperation(workingCopyIdentifier); } } @@ -287,14 +295,29 @@ export abstract class WorkingCopyBackupTracker extends Disposable { } if (workingCopyIdentifier) { - dispose(this.pendingBackupOperations.get(workingCopyIdentifier)); - this.pendingBackupOperations.delete(workingCopyIdentifier); + this.doClearPendingBackupOperation(workingCopyIdentifier, { cancel: true }); } } + private doClearPendingBackupOperation(workingCopyIdentifier: IWorkingCopyIdentifier, options?: { cancel: boolean }): void { + const pendingBackupOperation = this.pendingBackupOperations.get(workingCopyIdentifier); + if (!pendingBackupOperation) { + return; + } + + if (options?.cancel) { + pendingBackupOperation.cancel(); + } + + pendingBackupOperation.disposable.dispose(); + + this.pendingBackupOperations.delete(workingCopyIdentifier); + } + protected cancelBackupOperations(): void { - for (const [, disposable] of this.pendingBackupOperations) { - dispose(disposable); + for (const [, operation] of this.pendingBackupOperations) { + operation.cancel(); + operation.disposable.dispose(); } this.pendingBackupOperations.clear(); diff --git a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts index a689d217c5e..56e3fd5e9cd 100644 --- a/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts +++ b/src/vs/workbench/services/workingCopy/common/workingCopyHistoryService.ts @@ -814,7 +814,7 @@ export class NativeWorkingCopyHistoryService extends WorkingCopyHistoryService { if (!this.isRemotelyStored) { // Local: persist all on shutdown - this.lifecycleService.onWillShutdown(e => this.onWillShutdown(e)); + this._register(this.lifecycleService.onWillShutdown(e => this.onWillShutdown(e))); // Local: schedule persist on change this._register(Event.any(this.onDidAddEntry, this.onDidChangeEntry, this.onDidReplaceEntry, this.onDidRemoveEntry)(() => this.onDidChangeModels())); diff --git a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts index 1aa006ca6e6..5e597eaa2bc 100644 --- a/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts +++ b/src/vs/workbench/services/workingCopy/electron-sandbox/workingCopyBackupService.ts @@ -34,7 +34,7 @@ export class NativeWorkingCopyBackupService extends WorkingCopyBackupService { // Lifecycle: ensure to prolong the shutdown for as long // as pending backup operations have not finished yet. // Otherwise, we risk writing partial backups to disk. - this.lifecycleService.onWillShutdown(event => event.join(this.joinBackups(), { id: 'join.workingCopyBackups', label: localize('join.workingCopyBackups', "Backup working copies") })); + this._register(this.lifecycleService.onWillShutdown(event => event.join(this.joinBackups(), { id: 'join.workingCopyBackups', label: localize('join.workingCopyBackups', "Backup working copies") }))); } } diff --git a/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts index 307ed8eb379..c048929e21c 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/fileWorkingCopyManager.test.ts @@ -18,21 +18,20 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('FileWorkingCopyManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let manager: IFileWorkingCopyManager; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); accessor.fileService.registerProvider(Schemas.file, new TestInMemoryFileSystemProvider()); accessor.fileService.registerProvider(Schemas.vscodeRemote, new TestInMemoryFileSystemProvider()); - manager = new FileWorkingCopyManager( + manager = disposables.add(new FileWorkingCopyManager( 'testFileWorkingCopyType', new TestStoredFileWorkingCopyModelFactory(), new TestUntitledFileWorkingCopyModelFactory(), @@ -41,12 +40,11 @@ suite('FileWorkingCopyManager', () => { accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, accessor.environmentService, accessor.dialogService, accessor.decorationsService - ); + )); }); teardown(() => { - manager.dispose(); - disposables.dispose(); + disposables.clear(); }); test('onDidCreate, get, workingCopies', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts index b7f6501ab42..c745f135603 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts @@ -32,7 +32,7 @@ suite('ResourceWorkingCopy', function () { } - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.file('test/resource'); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -43,16 +43,14 @@ suite('ResourceWorkingCopy', function () { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('orphaned tracking', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts index df3106ac319..8d45d3c7ddf 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts @@ -129,7 +129,7 @@ suite('StoredFileWorkingCopy (with custom save)', function () { const factory = new TestStoredFileWorkingCopyModelWithCustomSaveFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.file('test/resource'); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -142,16 +142,14 @@ suite('StoredFileWorkingCopy (with custom save)', function () { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('save (custom implemented)', async () => { @@ -200,7 +198,7 @@ suite('StoredFileWorkingCopy', function () { const factory = new TestStoredFileWorkingCopyModelFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.file('test/resource'); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -213,16 +211,14 @@ suite('StoredFileWorkingCopy', function () { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('registers with working copy service', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts index cfb69f33e2e..08665525ef0 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts @@ -20,30 +20,28 @@ import { isWeb } from 'vs/base/common/platform'; suite('StoredFileWorkingCopyManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let manager: IStoredFileWorkingCopyManager; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - manager = new StoredFileWorkingCopyManager( + manager = disposables.add(new StoredFileWorkingCopyManager( 'testStoredFileWorkingCopyType', new TestStoredFileWorkingCopyModelFactory(), accessor.fileService, accessor.lifecycleService, accessor.labelService, accessor.logService, accessor.workingCopyFileService, accessor.workingCopyBackupService, accessor.uriIdentityService, accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService - ); + )); }); teardown(() => { - manager.dispose(); - disposables.dispose(); + disposables.clear(); }); test('resolve', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts index c98fad63dac..d38d34ce846 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts @@ -90,7 +90,7 @@ suite('UntitledFileWorkingCopy', () => { const factory = new TestUntitledFileWorkingCopyModelFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -113,16 +113,14 @@ suite('UntitledFileWorkingCopy', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('registers with working copy service', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts index c32b141d5f6..32799349d46 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopyManager.test.ts @@ -17,21 +17,20 @@ import { TestInMemoryFileSystemProvider, TestServiceAccessor, workbenchInstantia suite('UntitledFileWorkingCopyManager', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let manager: IFileWorkingCopyManager; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); accessor.fileService.registerProvider(Schemas.file, new TestInMemoryFileSystemProvider()); accessor.fileService.registerProvider(Schemas.vscodeRemote, new TestInMemoryFileSystemProvider()); - manager = new FileWorkingCopyManager( + manager = disposables.add(new FileWorkingCopyManager( 'testUntitledFileWorkingCopyType', new TestStoredFileWorkingCopyModelFactory(), new TestUntitledFileWorkingCopyModelFactory(), @@ -40,12 +39,11 @@ suite('UntitledFileWorkingCopyManager', () => { accessor.filesConfigurationService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService, accessor.pathService, accessor.environmentService, accessor.dialogService, accessor.decorationsService - ); + )); }); teardown(() => { - manager.dispose(); - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts index b3f94c4ab9c..501dd4dac49 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts @@ -27,7 +27,7 @@ suite('UntitledScratchpadWorkingCopy', () => { const factory = new TestUntitledFileWorkingCopyModelFactory(); - let disposables: DisposableStore; + const disposables = new DisposableStore(); const resource = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; @@ -50,16 +50,14 @@ suite('UntitledScratchpadWorkingCopy', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = createWorkingCopy(); + workingCopy = disposables.add(createWorkingCopy()); }); teardown(() => { - workingCopy.dispose(); - disposables.dispose(); + disposables.clear(); }); test('registers with working copy service', async () => { diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts index eab01d63a3b..ee95011f41f 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts @@ -24,13 +24,11 @@ import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices' import { CancellationToken } from 'vs/base/common/cancellation'; import { timeout } from 'vs/base/common/async'; import { BrowserWorkingCopyBackupTracker } from 'vs/workbench/services/workingCopy/browser/workingCopyBackupTracker'; -import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IWorkingCopyEditorHandler, IWorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService'; import { bufferToReadable, VSBuffer } from 'vs/base/common/buffer'; import { isWindows } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; -import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; suite('WorkingCopyBackupTracker (browser)', function () { let accessor: TestServiceAccessor; @@ -40,7 +38,15 @@ suite('WorkingCopyBackupTracker (browser)', function () { disposables.add(registerTestResourceEditor()); }); - teardown(() => { + teardown(async () => { + for (const copy of accessor.workingCopyService.workingCopies) { + await copy.revert(); + } + + for (const group of accessor.editorGroupService.groups) { + await group.closeAllEditors(); + } + disposables.clear(); }); @@ -85,10 +91,8 @@ suite('WorkingCopyBackupTracker (browser)', function () { } } - async function createTracker(): Promise<{ accessor: TestServiceAccessor; part: EditorPart; tracker: TestWorkingCopyBackupTracker; workingCopyBackupService: InMemoryTestWorkingCopyBackupService; instantiationService: IInstantiationService; cleanup: () => void }> { - const disposables = new DisposableStore(); - - const workingCopyBackupService = new InMemoryTestWorkingCopyBackupService(); + async function createTracker(): Promise<{ accessor: TestServiceAccessor; part: EditorPart; tracker: TestWorkingCopyBackupTracker; workingCopyBackupService: InMemoryTestWorkingCopyBackupService; instantiationService: IInstantiationService }> { + const workingCopyBackupService = disposables.add(new InMemoryTestWorkingCopyBackupService()); const instantiationService = workbenchInstantiationService(undefined, disposables); instantiationService.stub(IWorkingCopyBackupService, workingCopyBackupService); @@ -97,20 +101,18 @@ suite('WorkingCopyBackupTracker (browser)', function () { disposables.add(registerTestResourceEditor()); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); accessor = instantiationService.createInstance(TestServiceAccessor); const tracker = disposables.add(instantiationService.createInstance(TestWorkingCopyBackupTracker)); - return { accessor, part, tracker, workingCopyBackupService: workingCopyBackupService, instantiationService, cleanup: () => disposables.dispose() }; + return { accessor, part, tracker, workingCopyBackupService: workingCopyBackupService, instantiationService }; } async function untitledBackupTest(untitled: IUntitledTextResourceEditorInput = { resource: undefined }): Promise { - const { accessor, cleanup, workingCopyBackupService } = await createTracker(); + const { accessor, workingCopyBackupService } = await createTracker(); const untitledTextEditor = (await accessor.editorService.openEditor(untitled))?.input as UntitledTextEditorInput; @@ -129,8 +131,6 @@ suite('WorkingCopyBackupTracker (browser)', function () { await workingCopyBackupService.joinDiscardBackup(); assert.strictEqual(workingCopyBackupService.hasBackupSync(untitledTextModel), false); - - cleanup(); } test('Track backups (untitled)', function () { @@ -142,14 +142,14 @@ suite('WorkingCopyBackupTracker (browser)', function () { }); test('Track backups (custom)', async function () { - const { accessor, tracker, cleanup, workingCopyBackupService } = await createTracker(); + const { accessor, tracker, workingCopyBackupService } = await createTracker(); class TestBackupWorkingCopy extends TestWorkingCopy { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + disposables.add(accessor.workingCopyService.registerWorkingCopy(this)); } readonly backupDelay = 10; @@ -162,7 +162,7 @@ suite('WorkingCopyBackupTracker (browser)', function () { } const resource = toResource.call(this, '/path/custom.txt'); - const customWorkingCopy = new TestBackupWorkingCopy(resource); + const customWorkingCopy = disposables.add(new TestBackupWorkingCopy(resource)); // Normal customWorkingCopy.setDirty(true); @@ -188,29 +188,22 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.strictEqual(tracker.pendingBackupOperationCount, 1); await workingCopyBackupService.joinDiscardBackup(); assert.strictEqual(workingCopyBackupService.hasBackupSync(customWorkingCopy), false); - - customWorkingCopy.dispose(); - cleanup(); }); - async function restoreBackupsInit(): Promise<[TestWorkingCopyBackupTracker, TestServiceAccessor, IDisposable]> { + async function restoreBackupsInit(): Promise<[TestWorkingCopyBackupTracker, TestServiceAccessor]> { const fooFile = URI.file(isWindows ? 'c:\\Foo' : '/Foo'); const barFile = URI.file(isWindows ? 'c:\\Bar' : '/Bar'); const untitledFile1 = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); const untitledFile2 = URI.from({ scheme: Schemas.untitled, path: 'Untitled-2' }); - const disposables = new DisposableStore(); - - const workingCopyBackupService = new InMemoryTestWorkingCopyBackupService(); + const workingCopyBackupService = disposables.add(new InMemoryTestWorkingCopyBackupService()); const instantiationService = workbenchInstantiationService(undefined, disposables); instantiationService.stub(IWorkingCopyBackupService, workingCopyBackupService); const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); accessor = instantiationService.createInstance(TestServiceAccessor); @@ -230,11 +223,11 @@ suite('WorkingCopyBackupTracker (browser)', function () { accessor.lifecycleService.phase = LifecyclePhase.Restored; - return [tracker, accessor, disposables]; + return [tracker, accessor]; } test('Restore backups (basics, some handled)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); assert.strictEqual(tracker.getUnrestoredBackups().size, 0); @@ -256,7 +249,7 @@ suite('WorkingCopyBackupTracker (browser)', function () { createEditor: workingCopy => { createEditorCounter++; - return accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + return disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); } }); @@ -272,12 +265,10 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.ok(editor instanceof TestUntitledTextEditorInput); assert.strictEqual(editor.resolved, true); } - - dispose(disposables); }); test('Restore backups (basics, none handled)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); await tracker.testRestoreBackups({ handles: workingCopy => false, @@ -287,12 +278,10 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.strictEqual(accessor.editorService.count, 0); assert.strictEqual(tracker.getUnrestoredBackups().size, 4); - - dispose(disposables); }); test('Restore backups (basics, error case)', async function () { - const [tracker, , disposables] = await restoreBackupsInit(); + const [tracker] = await restoreBackupsInit(); try { await tracker.testRestoreBackups({ @@ -305,12 +294,10 @@ suite('WorkingCopyBackupTracker (browser)', function () { } assert.strictEqual(tracker.getUnrestoredBackups().size, 4); - - dispose(disposables); }); test('Restore backups (multiple handlers)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); const firstHandler = tracker.testRestoreBackups({ handles: workingCopy => { @@ -346,20 +333,18 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.ok(editor instanceof TestUntitledTextEditorInput); assert.strictEqual(editor.resolved, true); } - - dispose(disposables); }); test('Restore backups (editors already opened)', async function () { - const [tracker, accessor, disposables] = await restoreBackupsInit(); + const [tracker, accessor] = await restoreBackupsInit(); assert.strictEqual(tracker.getUnrestoredBackups().size, 0); let handlesCounter = 0; let isOpenCounter = 0; - const editor1 = accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); - const editor2 = accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + const editor1 = disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); + const editor2 = disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); await accessor.editorService.openEditors([{ editor: editor1 }, { editor: editor2 }]); @@ -396,7 +381,5 @@ suite('WorkingCopyBackupTracker (browser)', function () { assert.strictEqual(editor.resolved, true); } } - - dispose(disposables); }); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts index 26521dee61a..11bad8713a6 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyEditorService.test.ts @@ -6,12 +6,11 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; import { IWorkingCopyEditorHandler, WorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService'; -import { TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { createEditorPart, registerTestResourceEditor, TestEditorService, TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; @@ -28,12 +27,12 @@ suite('WorkingCopyEditorService', () => { }); test('registry - basics', () => { - const service = new WorkingCopyEditorService(new TestEditorService()); + const service = disposables.add(new WorkingCopyEditorService(new TestEditorService())); let handlerEvent: IWorkingCopyEditorHandler | undefined = undefined; - service.onDidRegisterHandler(handler => { + disposables.add(service.onDidRegisterHandler(handler => { handlerEvent = handler; - }); + })); const editorHandler: IWorkingCopyEditorHandler = { handles: workingCopy => false, @@ -41,11 +40,9 @@ suite('WorkingCopyEditorService', () => { createEditor: workingCopy => { throw new Error(); } }; - const disposable = service.registerHandler(editorHandler); + disposables.add(service.registerHandler(editorHandler)); assert.strictEqual(handlerEvent, editorHandler); - - disposable.dispose(); }); test('findEditor', async () => { @@ -55,14 +52,13 @@ suite('WorkingCopyEditorService', () => { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - instantiationService.stub(IWorkspaceTrustRequestService, new TestWorkspaceTrustRequestService(false)); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); const accessor = instantiationService.createInstance(TestServiceAccessor); - const service = new WorkingCopyEditorService(editorService); + const service = disposables.add(new WorkingCopyEditorService(editorService)); const resource = URI.parse('custom://some/folder/custom.txt'); - const testWorkingCopy = new TestWorkingCopy(resource, false, 'testWorkingCopyTypeId1'); + const testWorkingCopy = disposables.add(new TestWorkingCopy(resource, false, 'testWorkingCopyTypeId1')); assert.strictEqual(service.findEditor(testWorkingCopy), undefined); @@ -74,8 +70,8 @@ suite('WorkingCopyEditorService', () => { disposables.add(service.registerHandler(editorHandler)); - const editor1 = instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); - const editor2 = instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + const editor1 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); + const editor2 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); await editorService.openEditors([{ editor: editor1 }, { editor: editor2 }]); @@ -83,4 +79,6 @@ suite('WorkingCopyEditorService', () => { disposables.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts index ab966104874..14e3b835991 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts @@ -20,19 +20,18 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('WorkingCopyFileService', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.textFileService.files); }); teardown(() => { - (accessor.textFileService.files).dispose(); - disposables.dispose(); + disposables.clear(); }); test('create - dirty file', async function () { diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts index 8c381b4318b..92da56329ee 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts @@ -30,6 +30,8 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil import { generateUuid } from 'vs/base/common/uuid'; import { INativeWindowConfiguration } from 'vs/platform/window/common/window'; import product from 'vs/platform/product/common/product'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; const homeDir = URI.file('home').with({ scheme: Schemas.inMemory }); const tmpDir = URI.file('tmp').with({ scheme: Schemas.inMemory }); @@ -170,6 +172,8 @@ suite('WorkingCopyBackupService', () => { let service: NodeTestWorkingCopyBackupService; let fileService: IFileService; + const disposables = new DisposableStore(); + const workspaceResource = URI.file(isWindows ? 'c:\\workspace' : '/workspace'); const fooFile = URI.file(isWindows ? 'c:\\Foo' : '/Foo'); const customFile = URI.parse('customScheme://some/path'); @@ -184,7 +188,7 @@ suite('WorkingCopyBackupService', () => { workspacesJsonPath = joinPath(backupHome, 'workspaces.json'); workspaceBackupPath = joinPath(backupHome, hash(workspaceResource.fsPath).toString(16)); - service = new NodeTestWorkingCopyBackupService(testDir, workspaceBackupPath); + service = disposables.add(new NodeTestWorkingCopyBackupService(testDir, workspaceBackupPath)); fileService = service._fileService; await fileService.createFolder(backupHome); @@ -192,6 +196,10 @@ suite('WorkingCopyBackupService', () => { return fileService.writeFile(workspacesJsonPath, VSBuffer.fromString('')); }); + teardown(() => { + disposables.clear(); + }); + suite('hashIdentifier', () => { test('should correctly hash the identifier for untitled scheme URIs', () => { const uri = URI.from({ scheme: Schemas.untitled, path: 'Untitled-1' }); @@ -1296,4 +1304,6 @@ suite('WorkingCopyBackupService', () => { assert.ok(backups.every(backup => backup.typeId === '')); }); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts index 8b53c58a362..9d17566ae2d 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupTracker.test.ts @@ -16,7 +16,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ILogService } from 'vs/platform/log/common/log'; @@ -28,7 +28,7 @@ import { INativeHostService } from 'vs/platform/native/common/native'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { createEditorPart, registerTestFileEditor, TestBeforeShutdownEvent, TestEnvironmentService, TestFilesConfigurationService, TestFileService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { createEditorPart, registerTestFileEditor, TestBeforeShutdownEvent, TestEnvironmentService, TestFilesConfigurationService, TestFileService, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -82,8 +82,9 @@ suite('WorkingCopyBackupTracker (native)', function () { override dispose() { super.dispose(); - for (const [_, disposable] of this.pendingBackupOperations) { - disposable.dispose(); + for (const [_, pending] of this.pendingBackupOperations) { + pending.cancel(); + pending.disposable.dispose(); } } @@ -113,11 +114,10 @@ suite('WorkingCopyBackupTracker (native)', function () { let workspaceBackupPath: URI; let accessor: TestServiceAccessor; - let disposables: DisposableStore; + + const disposables = new DisposableStore(); setup(async () => { - disposables = new DisposableStore(); - testDir = URI.file(join(generateUuid(), 'vsctests', 'workingcopybackuptracker')).with({ scheme: Schemas.inMemory }); backupHome = joinPath(testDir, 'Backups'); const workspacesJsonPath = joinPath(backupHome, 'workspaces.json'); @@ -137,8 +137,8 @@ suite('WorkingCopyBackupTracker (native)', function () { return accessor.fileService.writeFile(workspacesJsonPath, VSBuffer.fromString('')); }); - teardown(async () => { - disposables.dispose(); + teardown(() => { + disposables.clear(); }); async function createTracker(autoSaveEnabled = false): Promise<{ accessor: TestServiceAccessor; part: EditorPart; tracker: TestWorkingCopyBackupTracker; instantiationService: IInstantiationService; cleanup: () => Promise }> { @@ -150,19 +150,19 @@ suite('WorkingCopyBackupTracker (native)', function () { } instantiationService.stub(IConfigurationService, configurationService); - instantiationService.stub(IFilesConfigurationService, new TestFilesConfigurationService( + instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService( instantiationService.createInstance(MockContextKeyService), configurationService, new TestContextService(TestWorkspace), TestEnvironmentService, - new UriIdentityService(new TestFileService()), - new TestFileService() - )); + disposables.add(new UriIdentityService(disposables.add(new TestFileService()))), + disposables.add(new TestFileService()) + ))); const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService: EditorService = instantiationService.createInstance(EditorService); + const editorService: EditorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); accessor = instantiationService.createInstance(TestServiceAccessor); @@ -170,8 +170,9 @@ suite('WorkingCopyBackupTracker (native)', function () { const tracker = instantiationService.createInstance(TestWorkingCopyBackupTracker); const cleanup = async () => { - // File changes could also schedule some backup operations so we need to wait for them before finishing the test - await accessor.workingCopyBackupService.waitForAllBackups(); + await accessor.workingCopyBackupService.waitForAllBackups(); // File changes could also schedule some backup operations so we need to wait for them before finishing the test + + await workbenchTeardown(instantiationService); part.dispose(); tracker.dispose(); @@ -356,7 +357,7 @@ suite('WorkingCopyBackupTracker (native)', function () { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + this._register(accessor.workingCopyService.registerWorkingCopy(this)); } override async backup(token: CancellationToken): Promise { @@ -365,7 +366,7 @@ suite('WorkingCopyBackupTracker (native)', function () { } const resource = toResource.call(this, '/path/custom.txt'); - const customWorkingCopy = new TestBackupWorkingCopy(resource); + const customWorkingCopy = disposables.add(new TestBackupWorkingCopy(resource)); customWorkingCopy.setDirty(true); const event = new TestBeforeShutdownEvent(); @@ -389,7 +390,7 @@ suite('WorkingCopyBackupTracker (native)', function () { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + this._register(accessor.workingCopyService.registerWorkingCopy(this)); } override capabilities = WorkingCopyCapabilities.Untitled | WorkingCopyCapabilities.Scratchpad; @@ -408,7 +409,7 @@ suite('WorkingCopyBackupTracker (native)', function () { } const resource = toResource.call(this, '/path/custom.txt'); - new TestBackupWorkingCopy(resource); + disposables.add(new TestBackupWorkingCopy(resource)); const event = new TestBeforeShutdownEvent(); event.reason = ShutdownReason.QUIT; @@ -716,7 +717,7 @@ suite('WorkingCopyBackupTracker (native)', function () { constructor(resource: URI) { super(resource); - accessor.workingCopyService.registerWorkingCopy(this); + this._register(accessor.workingCopyService.registerWorkingCopy(this)); } override capabilities = WorkingCopyCapabilities.Untitled | WorkingCopyCapabilities.Scratchpad; @@ -747,7 +748,7 @@ suite('WorkingCopyBackupTracker (native)', function () { accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL); const resource = toResource.call(this, '/path/custom.txt'); - new TestBackupWorkingCopy(resource); + disposables.add(new TestBackupWorkingCopy(resource)); const event = new TestBeforeShutdownEvent(); event.reason = shutdownReason; @@ -761,4 +762,6 @@ suite('WorkingCopyBackupTracker (native)', function () { await cleanup(); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts index 9d9094116bc..ff4f48a4af9 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test.ts @@ -23,6 +23,8 @@ import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFil import { generateUuid } from 'vs/base/common/uuid'; import { join } from 'vs/base/common/path'; import { VSBuffer } from 'vs/base/common/buffer'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryService { @@ -30,24 +32,20 @@ export class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryServi readonly _configurationService: TestConfigurationService; readonly _lifecycleService: TestLifecycleService; - constructor(fileService?: IFileService) { + constructor(disposables: DisposableStore, fileService?: IFileService) { const environmentService = TestEnvironmentService; const logService = new NullLogService(); if (!fileService) { - fileService = new FileService(logService); - fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); - fileService.registerProvider(Schemas.vscodeUserData, new InMemoryFileSystemProvider()); + fileService = disposables.add(new FileService(logService)); + disposables.add(fileService.registerProvider(Schemas.inMemory, disposables.add(new InMemoryFileSystemProvider()))); + disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new InMemoryFileSystemProvider()))); } const remoteAgentService = new TestRemoteAgentService(); - - const uriIdentityService = new UriIdentityService(fileService); - - const labelService = new LabelService(environmentService, new TestContextService(), new TestPathService(), new TestRemoteAgentService(), new TestStorageService(), new TestLifecycleService()); - - const lifecycleService = new TestLifecycleService(); - + const uriIdentityService = disposables.add(new UriIdentityService(fileService)); + const lifecycleService = disposables.add(new TestLifecycleService()); + const labelService = disposables.add(new LabelService(environmentService, new TestContextService(), new TestPathService(), new TestRemoteAgentService(), disposables.add(new TestStorageService()), lifecycleService)); const configurationService = new TestConfigurationService(); super(fileService, remoteAgentService, environmentService, uriIdentityService, labelService, lifecycleService, logService, configurationService); @@ -60,6 +58,8 @@ export class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryServi suite('WorkingCopyHistoryService', () => { + const disposables = new DisposableStore(); + let testDir: URI; let historyHome: URI; let workHome: URI; @@ -84,7 +84,7 @@ suite('WorkingCopyHistoryService', () => { historyHome = joinPath(testDir, 'User', 'History'); workHome = joinPath(testDir, 'work'); - service = new TestWorkingCopyHistoryService(); + service = disposables.add(new TestWorkingCopyHistoryService(disposables)); fileService = service._fileService; await fileService.createFolder(historyHome); @@ -118,15 +118,15 @@ suite('WorkingCopyHistoryService', () => { } teardown(() => { - service.dispose(); + disposables.clear(); }); test('addEntry', async () => { const addEvents: IWorkingCopyHistoryEvent[] = []; - service.onDidAddEntry(e => addEvents.push(e)); + disposables.add(service.onDidAddEntry(e => addEvents.push(e))); - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); // Add Entry works @@ -164,7 +164,7 @@ suite('WorkingCopyHistoryService', () => { // Invalid working copies are ignored - const workingCopy3 = new TestWorkingCopy(testFile2Path.with({ scheme: 'unsupported' })); + const workingCopy3 = disposables.add(new TestWorkingCopy(testFile2Path.with({ scheme: 'unsupported' }))); const entry3A = await addEntry({ resource: workingCopy3.resource }, CancellationToken.None, false); assert.ok(!entry3A); @@ -173,9 +173,9 @@ suite('WorkingCopyHistoryService', () => { test('renameEntry', async () => { const changeEvents: IWorkingCopyHistoryEvent[] = []; - service.onDidChangeEntry(e => changeEvents.push(e)); + disposables.add(service.onDidChangeEntry(e => changeEvents.push(e))); - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -200,7 +200,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 3); @@ -209,9 +209,9 @@ suite('WorkingCopyHistoryService', () => { test('removeEntry', async () => { const removeEvents: IWorkingCopyHistoryEvent[] = []; - service.onDidRemoveEntry(e => removeEvents.push(e)); + disposables.add(service.onDidRemoveEntry(e => removeEvents.push(e))); - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -242,14 +242,14 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 3); }); test('removeEntry - deletes history entries folder when last entry removed', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); let entry = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -261,7 +261,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); assert.strictEqual((await fileService.exists(dirname(entry.location))), true); @@ -278,17 +278,17 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); assert.strictEqual((await fileService.exists(dirname(entry.location))), false); }); test('removeAll', async () => { let removed = false; - service.onDidRemoveEntries(() => removed = true); + disposables.add(service.onDidRemoveEntries(() => removed = true)); - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -317,7 +317,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 0); @@ -326,8 +326,8 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - simple', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); let entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 0); @@ -355,8 +355,8 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - metadata preserved when stored', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const entry1 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy2.resource }, CancellationToken.None); @@ -370,7 +370,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); let entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 1); @@ -383,7 +383,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - corrupt meta.json is no problem', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -395,7 +395,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); const metaFile = joinPath(dirname(entry1.location), 'entries.json'); assert.ok((await fileService.exists(metaFile))); @@ -407,7 +407,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - missing entries from meta.json is no problem', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -420,7 +420,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); await fileService.del(entry1.location); @@ -430,7 +430,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - in-memory and on-disk entries are merged', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource, source: 'other-source' }, CancellationToken.None); @@ -443,7 +443,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); const entry3 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry4 = await addEntry({ resource: workingCopy1.resource, source: 'other-source' }, CancellationToken.None); @@ -457,7 +457,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getEntries - configured max entries respected', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); await addEntry({ resource: workingCopy1.resource }, CancellationToken.None); @@ -483,8 +483,8 @@ suite('WorkingCopyHistoryService', () => { }); test('getAll', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); let resources = await service.getAll(CancellationToken.None); assert.strictEqual(resources.length, 0); @@ -510,9 +510,9 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); - const workingCopy3 = new TestWorkingCopy(testFile3Path); + const workingCopy3 = disposables.add(new TestWorkingCopy(testFile3Path)); await addEntry({ resource: workingCopy3.resource, source: 'test-source' }, CancellationToken.None); resources = await service.getAll(CancellationToken.None); @@ -525,7 +525,7 @@ suite('WorkingCopyHistoryService', () => { }); test('getAll - ignores resource when no entries exist', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); @@ -545,7 +545,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); resources = await service.getAll(CancellationToken.None); assert.strictEqual(resources.length, 0); @@ -563,7 +563,7 @@ suite('WorkingCopyHistoryService', () => { } test('entries cleaned up on shutdown', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy1.resource, source: 'other-source' }, CancellationToken.None); @@ -585,7 +585,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); let entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 2); @@ -608,7 +608,7 @@ suite('WorkingCopyHistoryService', () => { // Resolve from file service fresh and verify again service.dispose(); - service = new TestWorkingCopyHistoryService(fileService); + service = disposables.add(new TestWorkingCopyHistoryService(disposables, fileService)); entries = await service.getEntries(workingCopy1.resource, CancellationToken.None); assert.strictEqual(entries.length, 3); @@ -619,9 +619,9 @@ suite('WorkingCopyHistoryService', () => { test('entries are merged when source is same', async () => { let replaced: IWorkingCopyHistoryEntry | undefined = undefined; - service.onDidReplaceEntry(e => replaced = e.entry); + disposables.add(service.onDidReplaceEntry(e => replaced = e.entry)); - const workingCopy1 = new TestWorkingCopy(testFile1Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); service._configurationService.setUserConfiguration('workbench.localHistory.mergeWindow', 1); @@ -648,7 +648,7 @@ suite('WorkingCopyHistoryService', () => { }); test('move entries (file rename)', async () => { - const workingCopy = new TestWorkingCopy(testFile1Path); + const workingCopy = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); @@ -695,8 +695,8 @@ suite('WorkingCopyHistoryService', () => { }); test('entries moved (folder rename)', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const entry1A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); @@ -782,4 +782,6 @@ suite('WorkingCopyHistoryService', () => { } } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts index 5bbe9b30267..ef04d7041db 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryTracker.test.ts @@ -5,14 +5,14 @@ import * as assert from 'assert'; import { Event } from 'vs/base/common/event'; -import { TestContextService, TestStorageService, TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; import { randomPath } from 'vs/base/common/extpath'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { WorkingCopyHistoryTracker } from 'vs/workbench/services/workingCopy/common/workingCopyHistoryTracker'; import { WorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; -import { TestEnvironmentService, TestFileService, TestLifecycleService, TestPathService, TestRemoteAgentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestFileService, TestPathService } from 'vs/workbench/test/browser/workbenchTestServices'; import { DeferredPromise } from 'vs/base/common/async'; import { IFileService } from 'vs/platform/files/common/files'; import { Schemas } from 'vs/base/common/network'; @@ -25,43 +25,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IWorkingCopyHistoryEntry, IWorkingCopyHistoryEntryDescriptor } from 'vs/workbench/services/workingCopy/common/workingCopyHistory'; import { assertIsDefined } from 'vs/base/common/types'; import { VSBuffer } from 'vs/base/common/buffer'; -import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { NativeWorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/common/workingCopyHistoryService'; -import { NullLogService } from 'vs/platform/log/common/log'; -import { FileService } from 'vs/platform/files/common/fileService'; -import { LabelService } from 'vs/workbench/services/label/common/labelService'; - -class TestWorkingCopyHistoryService extends NativeWorkingCopyHistoryService { - - readonly _fileService: IFileService; - readonly _configurationService: TestConfigurationService; - readonly _lifecycleService: TestLifecycleService; - - constructor(testDir: URI | string) { - const environmentService = TestEnvironmentService; - const logService = new NullLogService(); - const fileService = new FileService(logService); - - fileService.registerProvider(Schemas.vscodeUserData, new InMemoryFileSystemProvider()); - - const remoteAgentService = new TestRemoteAgentService(); - - const uriIdentityService = new UriIdentityService(fileService); - - const labelService = new LabelService(environmentService, new TestContextService(), new TestPathService(), new TestRemoteAgentService(), new TestStorageService(), new TestLifecycleService()); - - const lifecycleService = new TestLifecycleService(); - - const configurationService = new TestConfigurationService(); - - super(fileService, remoteAgentService, environmentService, uriIdentityService, labelService, lifecycleService, logService, configurationService); - - this._fileService = fileService; - this._configurationService = configurationService; - this._lifecycleService = lifecycleService; - } -} +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { TestWorkingCopyHistoryService } from 'vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyHistoryService.test'; suite('WorkingCopyHistoryTracker', () => { @@ -73,13 +39,14 @@ suite('WorkingCopyHistoryTracker', () => { let workingCopyService: WorkingCopyService; let fileService: IFileService; let configurationService: TestConfigurationService; - let inMemoryFileSystemDisposable: IDisposable; let tracker: WorkingCopyHistoryTracker; let testFile1Path: URI; let testFile2Path: URI; + const disposables = new DisposableStore(); + const testFile1PathContents = 'Hello Foo'; const testFile2PathContents = [ 'Lorem ipsum ', @@ -104,14 +71,12 @@ suite('WorkingCopyHistoryTracker', () => { historyHome = joinPath(testDir, 'User', 'History'); workHome = joinPath(testDir, 'work'); - workingCopyHistoryService = new TestWorkingCopyHistoryService(testDir); - workingCopyService = new WorkingCopyService(); + workingCopyHistoryService = disposables.add(new TestWorkingCopyHistoryService(disposables)); + workingCopyService = disposables.add(new WorkingCopyService()); fileService = workingCopyHistoryService._fileService; configurationService = workingCopyHistoryService._configurationService; - inMemoryFileSystemDisposable = fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); - - tracker = createTracker(); + tracker = disposables.add(createTracker()); await fileService.createFolder(historyHome); await fileService.createFolder(workHome); @@ -127,7 +92,7 @@ suite('WorkingCopyHistoryTracker', () => { return new WorkingCopyHistoryTracker( workingCopyService, workingCopyHistoryService, - new UriIdentityService(new TestFileService()), + disposables.add(new UriIdentityService(disposables.add(new TestFileService()))), new TestPathService(undefined, Schemas.file), configurationService, new UndoRedoService(new TestDialogService(), new TestNotificationService()), @@ -137,28 +102,23 @@ suite('WorkingCopyHistoryTracker', () => { } teardown(async () => { - workingCopyHistoryService.dispose(); - workingCopyService.dispose(); - tracker.dispose(); - await fileService.del(testDir, { recursive: true }); - - inMemoryFileSystemDisposable.dispose(); + disposables.clear(); }); test('history entry added on save', async () => { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const stat1 = await fileService.resolve(workingCopy1.resource, { resolveMetadata: true }); const stat2 = await fileService.resolve(workingCopy2.resource, { resolveMetadata: true }); - workingCopyService.registerWorkingCopy(workingCopy1); - workingCopyService.registerWorkingCopy(workingCopy2); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy1)); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy2)); const saveResult = new DeferredPromise(); let addedCounter = 0; - workingCopyHistoryService.onDidAddEntry(e => { + disposables.add(workingCopyHistoryService.onDidAddEntry(e => { if (isEqual(e.entry.workingCopy.resource, workingCopy1.resource) || isEqual(e.entry.workingCopy.resource, workingCopy2.resource)) { addedCounter++; @@ -166,7 +126,7 @@ suite('WorkingCopyHistoryTracker', () => { saveResult.complete(); } } - }); + })); await workingCopy1.save(undefined, stat1); await workingCopy2.save(undefined, stat2); @@ -185,7 +145,7 @@ suite('WorkingCopyHistoryTracker', () => { // Recreate to apply settings tracker.dispose(); - tracker = createTracker(); + tracker = disposables.add(createTracker()); return assertNoLocalHistoryEntryAddedWithSettingsConfigured(); }); @@ -197,17 +157,17 @@ suite('WorkingCopyHistoryTracker', () => { }); async function assertNoLocalHistoryEntryAddedWithSettingsConfigured(): Promise { - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const stat1 = await fileService.resolve(workingCopy1.resource, { resolveMetadata: true }); const stat2 = await fileService.resolve(workingCopy2.resource, { resolveMetadata: true }); - workingCopyService.registerWorkingCopy(workingCopy1); - workingCopyService.registerWorkingCopy(workingCopy2); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy1)); + disposables.add(workingCopyService.registerWorkingCopy(workingCopy2)); const saveResult = new DeferredPromise(); - workingCopyHistoryService.onDidAddEntry(e => { + disposables.add(workingCopyHistoryService.onDidAddEntry(e => { if (isEqual(e.entry.workingCopy.resource, workingCopy1.resource)) { assert.fail('Unexpected working copy history entry: ' + e.entry.workingCopy.resource.toString()); } @@ -215,7 +175,7 @@ suite('WorkingCopyHistoryTracker', () => { if (isEqual(e.entry.workingCopy.resource, workingCopy2.resource)) { saveResult.complete(); } - }); + })); await workingCopy1.save(undefined, stat1); await workingCopy2.save(undefined, stat2); @@ -226,7 +186,7 @@ suite('WorkingCopyHistoryTracker', () => { test('entries moved (file rename)', async () => { const entriesMoved = Event.toPromise(workingCopyHistoryService.onDidMoveEntries); - const workingCopy = new TestWorkingCopy(testFile1Path); + const workingCopy = disposables.add(new TestWorkingCopy(testFile1Path)); const entry1 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); const entry2 = await addEntry({ resource: workingCopy.resource, source: 'test-source' }, CancellationToken.None); @@ -272,8 +232,8 @@ suite('WorkingCopyHistoryTracker', () => { test('entries moved (folder rename)', async () => { const entriesMoved = Event.toPromise(workingCopyHistoryService.onDidMoveEntries); - const workingCopy1 = new TestWorkingCopy(testFile1Path); - const workingCopy2 = new TestWorkingCopy(testFile2Path); + const workingCopy1 = disposables.add(new TestWorkingCopy(testFile1Path)); + const workingCopy2 = disposables.add(new TestWorkingCopy(testFile2Path)); const entry1A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); const entry2A = await addEntry({ resource: workingCopy1.resource, source: 'test-source' }, CancellationToken.None); @@ -352,5 +312,6 @@ suite('WorkingCopyHistoryTracker', () => { } } }); -}); + ensureNoDisposablesAreLeakedInTestSuite(); +}); diff --git a/src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts b/src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts deleted file mode 100644 index 636b664aa6f..00000000000 --- a/src/vs/workbench/services/workspaces/test/common/testWorkspaceTrustService.ts +++ /dev/null @@ -1,144 +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 { IDisposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo, WorkspaceTrustRequestOptions, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; - - -export class TestWorkspaceTrustEnablementService implements IWorkspaceTrustEnablementService { - _serviceBrand: undefined; - - constructor(private isEnabled: boolean = true) { } - - isWorkspaceTrustEnabled(): boolean { - return this.isEnabled; - } -} - -export class TestWorkspaceTrustManagementService implements IWorkspaceTrustManagementService { - _serviceBrand: undefined; - - private _onDidChangeTrust = new Emitter(); - onDidChangeTrust = this._onDidChangeTrust.event; - - private _onDidChangeTrustedFolders = new Emitter(); - onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event; - - private _onDidInitiateWorkspaceTrustRequestOnStartup = new Emitter(); - onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; - - - constructor( - private trusted: boolean = true - ) { } - - get acceptsOutOfWorkspaceFiles(): boolean { - throw new Error('Method not implemented.'); - } - - set acceptsOutOfWorkspaceFiles(value: boolean) { - throw new Error('Method not implemented.'); - } - - addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable { - throw new Error('Method not implemented.'); - } - - getTrustedUris(): URI[] { - throw new Error('Method not implemented.'); - } - - setParentFolderTrust(trusted: boolean): Promise { - throw new Error('Method not implemented.'); - } - - getUriTrustInfo(uri: URI): Promise { - throw new Error('Method not implemented.'); - } - - async setTrustedUris(folders: URI[]): Promise { - throw new Error('Method not implemented.'); - } - - async setUrisTrust(uris: URI[], trusted: boolean): Promise { - throw new Error('Method not implemented.'); - } - - canSetParentFolderTrust(): boolean { - throw new Error('Method not implemented.'); - } - - canSetWorkspaceTrust(): boolean { - throw new Error('Method not implemented.'); - } - - isWorkspaceTrusted(): boolean { - return this.trusted; - } - - isWorkspaceTrustForced(): boolean { - return false; - } - - get workspaceTrustInitialized(): Promise { - return Promise.resolve(); - } - - get workspaceResolved(): Promise { - return Promise.resolve(); - } - - async setWorkspaceTrust(trusted: boolean): Promise { - if (this.trusted !== trusted) { - this.trusted = trusted; - this._onDidChangeTrust.fire(this.trusted); - } - } -} - -export class TestWorkspaceTrustRequestService implements IWorkspaceTrustRequestService { - _serviceBrand: any; - - private readonly _onDidInitiateOpenFilesTrustRequest = new Emitter(); - readonly onDidInitiateOpenFilesTrustRequest = this._onDidInitiateOpenFilesTrustRequest.event; - - private readonly _onDidInitiateWorkspaceTrustRequest = new Emitter(); - readonly onDidInitiateWorkspaceTrustRequest = this._onDidInitiateWorkspaceTrustRequest.event; - - private readonly _onDidInitiateWorkspaceTrustRequestOnStartup = new Emitter(); - readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; - - constructor(private readonly _trusted: boolean) { } - - requestOpenUrisHandler = async (uris: URI[]) => { - return WorkspaceTrustUriResponse.Open; - }; - - requestOpenFilesTrust(uris: URI[]): Promise { - return this.requestOpenUrisHandler(uris); - } - - async completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse: boolean): Promise { - throw new Error('Method not implemented.'); - } - - cancelWorkspaceTrustRequest(): void { - throw new Error('Method not implemented.'); - } - - async completeWorkspaceTrustRequest(trusted?: boolean): Promise { - throw new Error('Method not implemented.'); - } - - async requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise { - return this._trusted; - } - - requestWorkspaceTrustOnStartup(): void { - throw new Error('Method not implemented.'); - } -} diff --git a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts index 037330ae50e..a39caa0061e 100644 --- a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts +++ b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts @@ -21,8 +21,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { WorkspaceTrustEnablementService, WorkspaceTrustManagementService, WORKSPACE_TRUST_STORAGE_KEY } from 'vs/workbench/services/workspaces/common/workspaceTrust'; -import { TestWorkspaceTrustEnablementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; -import { TestContextService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestStorageService, TestWorkspaceTrustEnablementService } from 'vs/workbench/test/common/workbenchTestServices'; suite('Workspace Trust', () => { let instantiationService: TestInstantiationService; diff --git a/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts index fbc271d79e1..d94e0412166 100644 --- a/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts @@ -36,14 +36,10 @@ suite('Diff editor input', () => { } } - let disposables: DisposableStore; - - setup(() => { - disposables = new DisposableStore(); - }); + const disposables = new DisposableStore(); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', () => { diff --git a/src/vs/workbench/test/browser/parts/editor/editor.test.ts b/src/vs/workbench/test/browser/parts/editor/editor.test.ts index a49e5dd00c1..ced0b141475 100644 --- a/src/vs/workbench/test/browser/parts/editor/editor.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editor.test.ts @@ -12,7 +12,7 @@ import { workbenchInstantiationService, TestServiceAccessor, TestEditorInput, re import { Schemas } from 'vs/base/common/network'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { whenEditorClosed } from 'vs/workbench/browser/editor'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -39,22 +39,11 @@ suite('Workbench editor utils', () => { let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; - async function createServices(): Promise { - const instantiationService = workbenchInstantiationService(undefined, disposables); - - const part = await createEditorPart(instantiationService, disposables); - instantiationService.stub(IEditorGroupsService, part); - - const editorService = instantiationService.createInstance(EditorService); - instantiationService.stub(IEditorService, editorService); - - return instantiationService.createInstance(TestServiceAccessor); - } - setup(() => { instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.untitledTextEditorService); disposables.add(registerTestFileEditor()); disposables.add(registerTestSideBySideEditor()); disposables.add(registerTestResourceEditor()); @@ -62,8 +51,6 @@ suite('Workbench editor utils', () => { }); teardown(() => { - accessor.untitledTextEditorService.dispose(); - disposables.clear(); }); @@ -99,8 +86,8 @@ suite('Workbench editor utils', () => { }); test('EditorInputCapabilities', () => { - const testInput1 = new TestFileEditorInput(URI.file('resource1'), 'testTypeId'); - const testInput2 = new TestFileEditorInput(URI.file('resource2'), 'testTypeId'); + const testInput1 = disposables.add(new TestFileEditorInput(URI.file('resource1'), 'testTypeId')); + const testInput2 = disposables.add(new TestFileEditorInput(URI.file('resource2'), 'testTypeId')); testInput1.capabilities = EditorInputCapabilities.None; assert.strictEqual(testInput1.hasCapability(EditorInputCapabilities.None), true); @@ -162,7 +149,7 @@ suite('Workbench editor utils', () => { assert.ok(!EditorResourceAccessor.getCanonicalUri(null!)); assert.ok(!EditorResourceAccessor.getOriginalUri(null!)); - const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const untitled = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(untitled)?.toString(), untitled.resource.toString()); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(untitled, { supportSideBySide: SideBySideEditor.PRIMARY })?.toString(), untitled.resource.toString()); @@ -182,7 +169,7 @@ suite('Workbench editor utils', () => { assert.strictEqual(EditorResourceAccessor.getOriginalUri(untitled, { filterByScheme: [Schemas.file, Schemas.untitled] })?.toString(), untitled.resource.toString()); assert.ok(!EditorResourceAccessor.getOriginalUri(untitled, { filterByScheme: Schemas.file })); - const file = new TestEditorInput(URI.file('/some/path.txt'), 'editorResourceFileTest'); + const file = disposables.add(new TestEditorInput(URI.file('/some/path.txt'), 'editorResourceFileTest')); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(file)?.toString(), file.resource.toString()); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(file, { supportSideBySide: SideBySideEditor.PRIMARY })?.toString(), file.resource.toString()); @@ -246,7 +233,7 @@ suite('Workbench editor utils', () => { const resource = URI.file('/some/path.txt'); const preferredResource = URI.file('/some/PATH.txt'); - const fileWithPreferredResource = new TestEditorInputWithPreferredResource(URI.file('/some/path.txt'), URI.file('/some/PATH.txt'), 'editorResourceFileTest'); + const fileWithPreferredResource = disposables.add(new TestEditorInputWithPreferredResource(URI.file('/some/path.txt'), URI.file('/some/PATH.txt'), 'editorResourceFileTest')); assert.strictEqual(EditorResourceAccessor.getCanonicalUri(fileWithPreferredResource)?.toString(), resource.toString()); assert.strictEqual(EditorResourceAccessor.getOriginalUri(fileWithPreferredResource)?.toString(), preferredResource.toString()); @@ -363,13 +350,13 @@ suite('Workbench editor utils', () => { assert.strictEqual(isEditorIdentifier(undefined), false); assert.strictEqual(isEditorIdentifier('undefined'), false); - const testInput1 = new TestFileEditorInput(URI.file('resource1'), 'testTypeId'); + const testInput1 = disposables.add(new TestFileEditorInput(URI.file('resource1'), 'testTypeId')); assert.strictEqual(isEditorIdentifier(testInput1), false); assert.strictEqual(isEditorIdentifier({ editor: testInput1, groupId: 3 }), true); }); test('isEditorInputWithOptionsAndGroup', () => { - const editorInput = new TestFileEditorInput(URI.file('resource1'), 'testTypeId'); + const editorInput = disposables.add(new TestFileEditorInput(URI.file('resource1'), 'testTypeId')); assert.strictEqual(isEditorInput(editorInput), true); assert.strictEqual(isEditorInputWithOptions(editorInput), false); assert.strictEqual(isEditorInputWithOptionsAndGroup(editorInput), false); @@ -434,6 +421,18 @@ suite('Workbench editor utils', () => { return testWhenEditorClosed(false, true, toResource.call(this, '/path/index.txt'), toResource.call(this, '/test.html')); }); + async function createServices(): Promise { + const instantiationService = workbenchInstantiationService(undefined, disposables); + + const part = await createEditorPart(instantiationService, disposables); + instantiationService.stub(IEditorGroupsService, part); + + const editorService = disposables.add(instantiationService.createInstance(EditorService)); + instantiationService.stub(IEditorService, editorService); + + return instantiationService.createInstance(TestServiceAccessor); + } + async function testWhenEditorClosed(sideBySide: boolean, custom: boolean, ...resources: URI[]): Promise { const accessor = await createServices(); @@ -453,4 +452,6 @@ suite('Workbench editor utils', () => { await closedPromise; } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts index 6187bda979f..474fe6c56bb 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts @@ -15,18 +15,17 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('TextDiffEditorModel', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts index 9d1a741e97c..3fe93c47cf6 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts @@ -22,7 +22,7 @@ suite('EditorInput', () => { let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; - let disposables: DisposableStore; + const disposables = new DisposableStore(); const testResource: URI = URI.from({ scheme: 'random', path: '/path' }); const untypedResourceEditorInput: IResourceEditorInput = { resource: testResource, options: { override: DEFAULT_EDITOR_ASSOCIATION.id } }; @@ -52,7 +52,6 @@ suite('EditorInput', () => { }; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); @@ -74,7 +73,7 @@ suite('EditorInput', () => { }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); class MyEditorInput extends EditorInput { diff --git a/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts b/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts index 8baa06d4b3d..380a2ad5ad0 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorPane.test.ts @@ -19,16 +19,16 @@ import { URI } from 'vs/base/common/uri'; import { EditorPaneDescriptor, EditorPaneRegistry } from 'vs/workbench/browser/editor'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEditorModel } from 'vs/platform/editor/common/editor'; -import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; -import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { TestStorageService, TestWorkspaceTrustManagementService } from 'vs/workbench/test/common/workbenchTestServices'; import { extUri } from 'vs/base/common/resources'; import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { TestWorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const NullThemeService = new TestThemeService(); @@ -37,8 +37,11 @@ const editorInputRegistry: IEditorFactoryRegistry = Registry.as(EditorExtensions class TestEditor extends EditorPane { - constructor(@ITelemetryService telemetryService: ITelemetryService) { - super('TestEditor', NullTelemetryService, NullThemeService, new TestStorageService()); + constructor() { + const disposables = new DisposableStore(); + super('TestEditor', NullTelemetryService, NullThemeService, disposables.add(new TestStorageService())); + + this._register(disposables); } override getId(): string { return 'testEditor'; } @@ -46,10 +49,13 @@ class TestEditor extends EditorPane { protected createEditor(): any { } } -export class OtherTestEditor extends EditorPane { +class OtherTestEditor extends EditorPane { - constructor(@ITelemetryService telemetryService: ITelemetryService) { - super('testOtherEditor', NullTelemetryService, NullThemeService, new TestStorageService()); + constructor() { + const disposables = new DisposableStore(); + super('testOtherEditor', NullTelemetryService, NullThemeService, disposables.add(new TestStorageService())); + + this._register(disposables); } override getId(): string { return 'testOtherEditor'; } @@ -106,9 +112,15 @@ class TestResourceEditorInput extends TextResourceEditorInput { } suite('EditorPane', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('EditorPane API', async () => { - const editor = new TestEditor(NullTelemetryService); - const input = new OtherTestInput(); + const editor = new TestEditor(); + const input = disposables.add(new OtherTestInput()); const options = {}; assert(!editor.isVisible()); @@ -120,9 +132,6 @@ suite('EditorPane', () => { editor.setVisible(true, group); assert(editor.isVisible()); assert.strictEqual(editor.group, group); - input.onWillDispose(() => { - assert(false); - }); editor.dispose(); editor.clearInput(); editor.setVisible(false, group); @@ -144,57 +153,46 @@ suite('EditorPane', () => { const oldEditorsCnt = editorRegistry.getEditorPanes().length; const oldInputCnt = editorRegistry.getEditors().length; - const dispose1 = editorRegistry.registerEditorPane(editorDescriptor1, [new SyncDescriptor(TestInput)]); - const dispose2 = editorRegistry.registerEditorPane(editorDescriptor2, [new SyncDescriptor(TestInput), new SyncDescriptor(OtherTestInput)]); + disposables.add(editorRegistry.registerEditorPane(editorDescriptor1, [new SyncDescriptor(TestInput)])); + disposables.add(editorRegistry.registerEditorPane(editorDescriptor2, [new SyncDescriptor(TestInput), new SyncDescriptor(OtherTestInput)])); assert.strictEqual(editorRegistry.getEditorPanes().length, oldEditorsCnt + 2); assert.strictEqual(editorRegistry.getEditors().length, oldInputCnt + 3); - assert.strictEqual(editorRegistry.getEditorPane(new TestInput()), editorDescriptor2); - assert.strictEqual(editorRegistry.getEditorPane(new OtherTestInput()), editorDescriptor2); + assert.strictEqual(editorRegistry.getEditorPane(disposables.add(new TestInput())), editorDescriptor2); + assert.strictEqual(editorRegistry.getEditorPane(disposables.add(new OtherTestInput())), editorDescriptor2); assert.strictEqual(editorRegistry.getEditorPaneByType('id1'), editorDescriptor1); assert.strictEqual(editorRegistry.getEditorPaneByType('id2'), editorDescriptor2); assert(!editorRegistry.getEditorPaneByType('id3')); - - dispose([dispose1, dispose2]); }); test('Editor Pane Lookup favors specific class over superclass (match on specific class)', function () { const d1 = EditorPaneDescriptor.create(TestEditor, 'id1', 'name'); - const disposables = new DisposableStore(); - disposables.add(registerTestResourceEditor()); disposables.add(editorRegistry.registerEditorPane(d1, [new SyncDescriptor(TestResourceEditorInput)])); const inst = workbenchInstantiationService(undefined, disposables); - const editor = editorRegistry.getEditorPane(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined))!.instantiate(inst); + const editor = disposables.add(editorRegistry.getEditorPane(disposables.add(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined)))!.instantiate(inst)); assert.strictEqual(editor.getId(), 'testEditor'); - const otherEditor = editorRegistry.getEditorPane(inst.createInstance(TextResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined))!.instantiate(inst); + const otherEditor = disposables.add(editorRegistry.getEditorPane(disposables.add(inst.createInstance(TextResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined)))!.instantiate(inst)); assert.strictEqual(otherEditor.getId(), 'workbench.editors.textResourceEditor'); - - disposables.dispose(); }); test('Editor Pane Lookup favors specific class over superclass (match on super class)', function () { - const disposables = new DisposableStore(); - const inst = workbenchInstantiationService(undefined, disposables); disposables.add(registerTestResourceEditor()); - const editor = editorRegistry.getEditorPane(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined))!.instantiate(inst); + const editor = disposables.add(editorRegistry.getEditorPane(disposables.add(inst.createInstance(TestResourceEditorInput, URI.file('/fake'), 'fake', '', undefined, undefined)))!.instantiate(inst)); assert.strictEqual('workbench.editors.textResourceEditor', editor.getId()); - - disposables.dispose(); }); test('Editor Input Serializer', function () { - const disposables = new DisposableStore(); - const testInput = new TestEditorInput(URI.file('/fake'), 'testTypeId'); + const testInput = disposables.add(new TestEditorInput(URI.file('/fake'), 'testTypeId')); workbenchInstantiationService(undefined, disposables).invokeFunction(accessor => editorInputRegistry.start(accessor)); disposables.add(editorInputRegistry.registerEditorSerializer(testInput.typeId, TestInputSerializer)); @@ -206,8 +204,6 @@ suite('EditorPane', () => { // throws when registering serializer for same type assert.throws(() => editorInputRegistry.registerEditorSerializer(testInput.typeId, TestInputSerializer)); - - disposables.dispose(); }); test('EditorMemento - basics', function () { @@ -228,7 +224,7 @@ suite('EditorPane', () => { } const rawMemento = Object.create(null); - let memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + let memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); let res = memento.loadEditorState(testGroup0, URI.file('/A')); assert.ok(!res); @@ -263,7 +259,7 @@ suite('EditorPane', () => { memento.saveState(); - memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); assert.ok(memento.loadEditorState(testGroup0, URI.file('/C'))); assert.ok(memento.loadEditorState(testGroup0, URI.file('/D'))); assert.ok(memento.loadEditorState(testGroup0, URI.file('/E'))); @@ -289,7 +285,7 @@ suite('EditorPane', () => { interface TestViewState { line: number } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); memento.saveEditorState(testGroup0, URI.file('/some/folder/file-1.txt'), { line: 1 }); memento.saveEditorState(testGroup0, URI.file('/some/folder/file-2.txt'), { line: 2 }); @@ -332,9 +328,9 @@ suite('EditorPane', () => { } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService()); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService())); - const testInputA = new TestEditorInput(URI.file('/A')); + const testInputA = disposables.add(new TestEditorInput(URI.file('/A'))); let res = memento.loadEditorState(testGroup0, testInputA); assert.ok(!res); @@ -370,9 +366,9 @@ suite('EditorPane', () => { } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService()); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, new TestEditorGroupsService(), new TestTextResourceConfigurationService())); - const testInputA = new TestEditorInput(URI.file('/A')); + const testInputA = disposables.add(new TestEditorInput(URI.file('/A'))); let res = memento.loadEditorState(testGroup0, testInputA); assert.ok(!res); @@ -388,7 +384,7 @@ suite('EditorPane', () => { res = memento.loadEditorState(testGroup0, testInputA); assert.ok(res); - const testInputB = new TestEditorInput(URI.file('/B')); + const testInputB = disposables.add(new TestEditorInput(URI.file('/B'))); res = memento.loadEditorState(testGroup0, testInputB); assert.ok(!res); @@ -422,7 +418,7 @@ suite('EditorPane', () => { interface TestViewState { line: number } const rawMemento = Object.create(null); - const memento = new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService); + const memento = disposables.add(new EditorMemento('id', 'key', rawMemento, 3, editorGroupService, configurationService)); const resource = URI.file('/some/folder/file-1.txt'); memento.saveEditorState(testGroup0, resource, { line: 1 }); @@ -459,7 +455,7 @@ suite('EditorPane', () => { class TrustRequiredTestEditor extends EditorPane { constructor(@ITelemetryService telemetryService: ITelemetryService) { - super('TestEditor', NullTelemetryService, NullThemeService, new TestStorageService()); + super('TestEditor', NullTelemetryService, NullThemeService, disposables.add(new TestStorageService())); } override getId(): string { return 'trustRequiredTestEditor'; } @@ -484,17 +480,15 @@ suite('EditorPane', () => { } } - const disposables = new DisposableStore(); - const instantiationService = workbenchInstantiationService(undefined, disposables); - const workspaceTrustService = instantiationService.createInstance(TestWorkspaceTrustManagementService); + const workspaceTrustService = disposables.add(instantiationService.createInstance(TestWorkspaceTrustManagementService)); instantiationService.stub(IWorkspaceTrustManagementService, workspaceTrustService); workspaceTrustService.setWorkspaceTrust(false); const editorPart = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, editorPart); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); const group = editorPart.activeGroup; @@ -502,7 +496,7 @@ suite('EditorPane', () => { const editorDescriptor = EditorPaneDescriptor.create(TrustRequiredTestEditor, 'id1', 'name'); disposables.add(editorRegistry.registerEditorPane(editorDescriptor, [new SyncDescriptor(TrustRequiredTestInput)])); - const testInput = new TrustRequiredTestInput(); + const testInput = disposables.add(new TrustRequiredTestInput()); await group.openEditor(testInput); assert.strictEqual(group.activeEditorPane?.getId(), WorkspaceTrustRequiredPlaceholderEditor.ID); @@ -520,6 +514,8 @@ suite('EditorPane', () => { workspaceTrustService.setWorkspaceTrust(false); assert.strictEqual(await getEditorPaneIdAsync(), WorkspaceTrustRequiredPlaceholderEditor.ID); - dispose(disposables); + await group.closeAllEditors(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts index 2fd994d4639..d1a44ee58f6 100644 --- a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts @@ -16,7 +16,7 @@ import { IFilesConfigurationService } from 'vs/workbench/services/filesConfigura suite('ResourceEditorInput', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; class TestResourceEditorInput extends AbstractResourceEditorInput { @@ -34,12 +34,11 @@ suite('ResourceEditorInput', () => { } setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts index 9ace2cbba4c..7eeb6e81a60 100644 --- a/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts @@ -13,14 +13,10 @@ import { TestFileEditorInput, workbenchInstantiationService } from 'vs/workbench suite('SideBySideEditorInput', () => { - let disposables: DisposableStore; - - setup(() => { - disposables = new DisposableStore(); - }); + const disposables = new DisposableStore(); teardown(() => { - disposables.dispose(); + disposables.clear(); }); class MyEditorInput extends EditorInput { diff --git a/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts b/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts index f678591223a..4574694f503 100644 --- a/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/textEditorPane.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { workbenchInstantiationService, TestServiceAccessor, registerTestFileEditor, createEditorPart, TestTextFileEditor } from 'vs/workbench/test/browser/workbenchTestServices'; import { IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; @@ -35,7 +35,7 @@ suite('TextEditorPane', () => { const part = await createEditorPart(instantiationService, disposables); instantiationService.stub(IEditorGroupsService, part); - const editorService = instantiationService.createInstance(EditorService); + const editorService = disposables.add(instantiationService.createInstance(EditorService)); instantiationService.stub(IEditorService, editorService); return instantiationService.createInstance(TestServiceAccessor); @@ -50,16 +50,16 @@ suite('TextEditorPane', () => { assert.ok(pane && isEditorPaneWithSelection(pane)); const onDidFireSelectionEventOfEditType = new DeferredPromise(); - pane.onDidChangeSelection(e => { + disposables.add(pane.onDidChangeSelection(e => { if (e.reason === EditorPaneSelectionChangeReason.EDIT) { onDidFireSelectionEventOfEditType.complete(e); } - }); + })); // Changing model reports selection change // of EDIT kind - const model = await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel; + const model = disposables.add(await accessor.textFileService.files.resolve(resource) as IResolvedTextFileEditorModel); model.textEditorModel.setValue('Hello World'); const event = await onDidFireSelectionEventOfEditType.p; @@ -83,6 +83,9 @@ suite('TextEditorPane', () => { const newSelection = pane.getSelection(); assert.ok(newSelection); assert.strictEqual(newSelection.compare(selection), EditorPaneSelectionCompareResult.IDENTICAL); + + await model.revert(); + await pane.group?.closeAllEditors(); }); test('TextEditorPaneSelection', function () { @@ -96,4 +99,6 @@ suite('TextEditorPane', () => { assert.strictEqual(sel1.compare(sel3), EditorPaneSelectionCompareResult.DIFFERENT); assert.strictEqual(sel1.compare(sel4), EditorPaneSelectionCompareResult.DIFFERENT); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts index d2c732e94db..797e6c88c2f 100644 --- a/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts @@ -15,18 +15,18 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('TextResourceEditorInput', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); + let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('basics', async () => { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 1340c38c2de..3fa67419252 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -99,7 +99,7 @@ import { IInputBox, IInputOptions, IPickOptions, IQuickInputButton, IQuickInputS import { QuickInputService } from 'vs/workbench/services/quickinput/browser/quickInputService'; import { IListService } from 'vs/platform/list/browser/listService'; import { win32, posix } from 'vs/base/common/path'; -import { TestContextService, TestStorageService, TestTextResourcePropertiesService, TestExtensionService, TestProductService, createFileStat, TestLoggerService } from 'vs/workbench/test/common/workbenchTestServices'; +import { TestContextService, TestStorageService, TestTextResourcePropertiesService, TestExtensionService, TestProductService, createFileStat, TestLoggerService, TestWorkspaceTrustManagementService, TestWorkspaceTrustRequestService } from 'vs/workbench/test/common/workbenchTestServices'; import { IViewsService, IView, ViewContainer, ViewContainerLocation } from 'vs/workbench/common/views'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; @@ -121,7 +121,6 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { IEnterWorkspaceResult, IRecent, IRecentlyOpened, IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; -import { TestWorkspaceTrustManagementService, TestWorkspaceTrustRequestService } from 'vs/workbench/services/workspaces/test/common/testWorkspaceTrustService'; import { IExtensionTerminalProfile, IShellLaunchConfig, ITerminalBackend, ITerminalProfile, TerminalIcon, TerminalLocation, TerminalShellType } from 'vs/platform/terminal/common/terminal'; import { ICreateTerminalOptions, IDeserializedTerminalEditorInput, ITerminalEditorService, ITerminalGroup, ITerminalGroupService, ITerminalInstance, ITerminalInstanceService, TerminalEditorLocation } from 'vs/workbench/contrib/terminal/browser/terminal'; import { assertIsDefined } from 'vs/base/common/types'; @@ -188,14 +187,14 @@ Registry.as(EditorExtensions.EditorFactory).registerFile export class TestTextResourceEditor extends TextResourceEditor { protected override createEditorControl(parent: HTMLElement, configuration: any): void { - this.editorControl = this.instantiationService.createInstance(TestCodeEditor, parent, configuration, {}); + this.editorControl = this._register(this.instantiationService.createInstance(TestCodeEditor, parent, configuration, {})); } } export class TestTextFileEditor extends TextFileEditor { protected override createEditorControl(parent: HTMLElement, configuration: any): void { - this.editorControl = this.instantiationService.createInstance(TestCodeEditor, parent, configuration, { contributions: [] }); + this.editorControl = this._register(this.instantiationService.createInstance(TestCodeEditor, parent, configuration, { contributions: [] })); } setSelection(selection: Selection | undefined, reason: EditorPaneSelectionChangeReason): void { @@ -243,7 +242,7 @@ export function workbenchInstantiationService( }, disposables: Pick = new DisposableStore() ): TestInstantiationService { - const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, new TestLifecycleService()]))); + const instantiationService = disposables.add(new TestInstantiationService(new ServiceCollection([ILifecycleService, disposables.add(new TestLifecycleService())]))); instantiationService.stub(IEditorWorkerService, new TestEditorWorkerService()); instantiationService.stub(IWorkingCopyService, disposables.add(new TestWorkingCopyService())); @@ -285,15 +284,15 @@ export function workbenchInstantiationService( instantiationService.stub(IThemeService, themeService); instantiationService.stub(ILanguageConfigurationService, disposables.add(new TestLanguageConfigurationService())); instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); - const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); + const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : disposables.add(new TestFileService()); instantiationService.stub(IFileService, fileService); const uriIdentityService = new UriIdentityService(fileService); disposables.add(uriIdentityService); instantiationService.stub(IFilesConfigurationService, disposables.add(new TestFilesConfigurationService(contextKeyService, configService, workspaceContextService, environmentService, uriIdentityService, fileService))); - instantiationService.stub(IUriIdentityService, uriIdentityService); + instantiationService.stub(IUriIdentityService, disposables.add(uriIdentityService)); const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, disposables.add(new UserDataProfilesService(environmentService, fileService, uriIdentityService, new NullLogService()))); instantiationService.stub(IUserDataProfileService, disposables.add(new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService))); - instantiationService.stub(IWorkingCopyBackupService, overrides?.workingCopyBackupService ? overrides?.workingCopyBackupService(instantiationService) : new TestWorkingCopyBackupService()); + instantiationService.stub(IWorkingCopyBackupService, overrides?.workingCopyBackupService ? overrides?.workingCopyBackupService(instantiationService) : disposables.add(new TestWorkingCopyBackupService())); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(INotificationService, new TestNotificationService()); instantiationService.stub(IUntitledTextEditorService, disposables.add(instantiationService.createInstance(UntitledTextEditorService))); @@ -323,7 +322,8 @@ export function workbenchInstantiationService( const hoverService = instantiationService.stub(IHoverService, instantiationService.createInstance(TestHoverService)); instantiationService.stub(IQuickInputService, disposables.add(new QuickInputService(configService, instantiationService, keybindingService, contextKeyService, themeService, layoutService, hoverService))); instantiationService.stub(IWorkspacesService, new TestWorkspacesService()); - instantiationService.stub(IWorkspaceTrustManagementService, new TestWorkspaceTrustManagementService()); + instantiationService.stub(IWorkspaceTrustManagementService, disposables.add(new TestWorkspaceTrustManagementService())); + instantiationService.stub(IWorkspaceTrustRequestService, disposables.add(new TestWorkspaceTrustRequestService(false))); instantiationService.stub(ITerminalInstanceService, new TestTerminalInstanceService()); instantiationService.stub(IElevatedFileService, new BrowserElevatedFileService()); instantiationService.stub(IRemoteSocketFactoryService, new RemoteSocketFactoryService()); @@ -1195,17 +1195,20 @@ export class InMemoryTestWorkingCopyBackupService extends BrowserWorkingCopyBack discardedBackups: IWorkingCopyIdentifier[]; constructor() { + const disposables = new DisposableStore(); const environmentService = TestEnvironmentService; const logService = new NullLogService(); - const fileService = new FileService(logService); - fileService.registerProvider(Schemas.file, new InMemoryFileSystemProvider()); - fileService.registerProvider(Schemas.vscodeUserData, new InMemoryFileSystemProvider()); + const fileService = disposables.add(new FileService(logService)); + disposables.add(fileService.registerProvider(Schemas.file, disposables.add(new InMemoryFileSystemProvider()))); + disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new InMemoryFileSystemProvider()))); super(new TestContextService(TestWorkspace), environmentService, fileService, logService); this.backupResourceJoiners = []; this.discardBackupJoiners = []; this.discardedBackups = []; + + this._register(disposables); } testGetFileService(): IFileService { @@ -1246,26 +1249,26 @@ export class InMemoryTestWorkingCopyBackupService extends BrowserWorkingCopyBack } } -export class TestLifecycleService implements ILifecycleService { +export class TestLifecycleService extends Disposable implements ILifecycleService { declare readonly _serviceBrand: undefined; phase!: LifecyclePhase; startupKind!: StartupKind; - private readonly _onBeforeShutdown = new Emitter(); + private readonly _onBeforeShutdown = this._register(new Emitter()); get onBeforeShutdown(): Event { return this._onBeforeShutdown.event; } - private readonly _onBeforeShutdownError = new Emitter(); + private readonly _onBeforeShutdownError = this._register(new Emitter()); get onBeforeShutdownError(): Event { return this._onBeforeShutdownError.event; } - private readonly _onShutdownVeto = new Emitter(); + private readonly _onShutdownVeto = this._register(new Emitter()); get onShutdownVeto(): Event { return this._onShutdownVeto.event; } - private readonly _onWillShutdown = new Emitter(); + private readonly _onWillShutdown = this._register(new Emitter()); get onWillShutdown(): Event { return this._onWillShutdown.event; } - private readonly _onDidShutdown = new Emitter(); + private readonly _onDidShutdown = this._register(new Emitter()); get onDidShutdown(): Event { return this._onDidShutdown.event; } async when(): Promise { } @@ -1488,12 +1491,14 @@ export class TestEditorInput extends EditorInput { } export function registerTestEditor(id: string, inputs: SyncDescriptor[], serializerInputId?: string): IDisposable { + const disposables = new DisposableStore(); + class TestEditor extends EditorPane { private _scopedContextKeyService: IContextKeyService; constructor() { - super(id, NullTelemetryService, new TestThemeService(), new TestStorageService()); + super(id, NullTelemetryService, new TestThemeService(), disposables.add(new TestStorageService())); this._scopedContextKeyService = new MockContextKeyService(); } @@ -1512,8 +1517,6 @@ export function registerTestEditor(id: string, inputs: SyncDescriptor(Extensions.EditorPane).registerEditorPane(EditorPaneDescriptor.create(TestEditor, id, 'Test Editor Control'), inputs)); if (serializerInputId) { @@ -2089,3 +2092,22 @@ export class TestWebExtensionsScannerService implements IWebExtensionsScannerSer throw new Error('Method not implemented.'); } } + +export async function workbenchTeardown(instantiationService: IInstantiationService): Promise { + return instantiationService.invokeFunction(async accessor => { + const workingCopyService = accessor.get(IWorkingCopyService); + const editorGroupService = accessor.get(IEditorGroupsService); + + for (const workingCopy of workingCopyService.workingCopies) { + await workingCopy.revert(); + } + + for (const group of editorGroupService.groups) { + await group.closeAllEditors(); + } + + for (const group of editorGroupService.groups) { + editorGroupService.removeGroup(group); + } + }); +} diff --git a/src/vs/workbench/test/common/workbenchTestServices.ts b/src/vs/workbench/test/common/workbenchTestServices.ts index 627f0ea0b77..117a4412509 100644 --- a/src/vs/workbench/test/common/workbenchTestServices.ts +++ b/src/vs/workbench/test/common/workbenchTestServices.ts @@ -28,6 +28,7 @@ import { IResourceEditorInput } from 'vs/platform/editor/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { AutoSaveMode, IAutoSaveConfiguration, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { IWorkspaceTrustEnablementService, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService, IWorkspaceTrustTransitionParticipant, IWorkspaceTrustUriInfo, WorkspaceTrustRequestOptions, WorkspaceTrustUriResponse } from 'vs/platform/workspace/common/workspaceTrust'; export class TestLoggerService extends AbstractLoggerService { constructor(logsHome?: URI) { @@ -315,3 +316,141 @@ export const NullFilesConfigurationService = new class implements IFilesConfigur async updateReadonly(resource: URI, readonly: boolean | 'toggle' | 'reset'): Promise { } preventSaveConflicts(resource: URI, language?: string | undefined): boolean { throw new Error('Method not implemented.'); } }; + +export class TestWorkspaceTrustEnablementService implements IWorkspaceTrustEnablementService { + _serviceBrand: undefined; + + constructor(private isEnabled: boolean = true) { } + + isWorkspaceTrustEnabled(): boolean { + return this.isEnabled; + } +} + +export class TestWorkspaceTrustManagementService extends Disposable implements IWorkspaceTrustManagementService { + _serviceBrand: undefined; + + private _onDidChangeTrust = this._register(new Emitter()); + onDidChangeTrust = this._onDidChangeTrust.event; + + private _onDidChangeTrustedFolders = this._register(new Emitter()); + onDidChangeTrustedFolders = this._onDidChangeTrustedFolders.event; + + private _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter()); + onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; + + + constructor( + private trusted: boolean = true + ) { + super(); + } + + get acceptsOutOfWorkspaceFiles(): boolean { + throw new Error('Method not implemented.'); + } + + set acceptsOutOfWorkspaceFiles(value: boolean) { + throw new Error('Method not implemented.'); + } + + addWorkspaceTrustTransitionParticipant(participant: IWorkspaceTrustTransitionParticipant): IDisposable { + throw new Error('Method not implemented.'); + } + + getTrustedUris(): URI[] { + throw new Error('Method not implemented.'); + } + + setParentFolderTrust(trusted: boolean): Promise { + throw new Error('Method not implemented.'); + } + + getUriTrustInfo(uri: URI): Promise { + throw new Error('Method not implemented.'); + } + + async setTrustedUris(folders: URI[]): Promise { + throw new Error('Method not implemented.'); + } + + async setUrisTrust(uris: URI[], trusted: boolean): Promise { + throw new Error('Method not implemented.'); + } + + canSetParentFolderTrust(): boolean { + throw new Error('Method not implemented.'); + } + + canSetWorkspaceTrust(): boolean { + throw new Error('Method not implemented.'); + } + + isWorkspaceTrusted(): boolean { + return this.trusted; + } + + isWorkspaceTrustForced(): boolean { + return false; + } + + get workspaceTrustInitialized(): Promise { + return Promise.resolve(); + } + + get workspaceResolved(): Promise { + return Promise.resolve(); + } + + async setWorkspaceTrust(trusted: boolean): Promise { + if (this.trusted !== trusted) { + this.trusted = trusted; + this._onDidChangeTrust.fire(this.trusted); + } + } +} + +export class TestWorkspaceTrustRequestService extends Disposable implements IWorkspaceTrustRequestService { + _serviceBrand: any; + + private readonly _onDidInitiateOpenFilesTrustRequest = this._register(new Emitter()); + readonly onDidInitiateOpenFilesTrustRequest = this._onDidInitiateOpenFilesTrustRequest.event; + + private readonly _onDidInitiateWorkspaceTrustRequest = this._register(new Emitter()); + readonly onDidInitiateWorkspaceTrustRequest = this._onDidInitiateWorkspaceTrustRequest.event; + + private readonly _onDidInitiateWorkspaceTrustRequestOnStartup = this._register(new Emitter()); + readonly onDidInitiateWorkspaceTrustRequestOnStartup = this._onDidInitiateWorkspaceTrustRequestOnStartup.event; + + constructor(private readonly _trusted: boolean) { + super(); + } + + requestOpenUrisHandler = async (uris: URI[]) => { + return WorkspaceTrustUriResponse.Open; + }; + + requestOpenFilesTrust(uris: URI[]): Promise { + return this.requestOpenUrisHandler(uris); + } + + async completeOpenFilesTrustRequest(result: WorkspaceTrustUriResponse, saveResponse: boolean): Promise { + throw new Error('Method not implemented.'); + } + + cancelWorkspaceTrustRequest(): void { + throw new Error('Method not implemented.'); + } + + async completeWorkspaceTrustRequest(trusted?: boolean): Promise { + throw new Error('Method not implemented.'); + } + + async requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise { + return this._trusted; + } + + requestWorkspaceTrustOnStartup(): void { + throw new Error('Method not implemented.'); + } +} diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index 1a40dc62bd5..b88818f68fa 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -8,7 +8,7 @@ import { workbenchInstantiationService as browserWorkbenchInstantiationService, import { ISharedProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { INativeHostService, IOSProperties, IOSStatistics } from 'vs/platform/native/common/native'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IFileDialogService, INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; @@ -52,11 +52,8 @@ export class TestSharedProcessService implements ISharedProcessService { declare readonly _serviceBrand: undefined; createRawConnection(): never { throw new Error('Not Implemented'); } - getChannel(channelName: string): any { return undefined; } - registerChannel(channelName: string, channel: any): void { } - notifyRestored(): void { } } @@ -178,7 +175,7 @@ export function workbenchInstantiationService(overrides?: { textEditorService?: (instantiationService: IInstantiationService) => ITextEditorService; }, disposables = new DisposableStore()): ITestInstantiationService { const instantiationService = browserWorkbenchInstantiationService({ - workingCopyBackupService: (instantiationService: IInstantiationService) => new TestNativeWorkingCopyBackupService(), + workingCopyBackupService: () => disposables.add(new TestNativeWorkingCopyBackupService()), ...overrides }, disposables); @@ -216,7 +213,7 @@ export class TestNativeTextFileServiceWithEncodingOverrides extends NativeTextFi } } -export class TestNativeWorkingCopyBackupService extends NativeWorkingCopyBackupService { +export class TestNativeWorkingCopyBackupService extends NativeWorkingCopyBackupService implements IDisposable { private backupResourceJoiners: Function[]; private discardBackupJoiners: Function[]; @@ -231,15 +228,18 @@ export class TestNativeWorkingCopyBackupService extends NativeWorkingCopyBackupS const lifecycleService = new TestLifecycleService(); super(environmentService as any, fileService, logService, lifecycleService); - const inMemoryFileSystemProvider = new InMemoryFileSystemProvider(); - fileService.registerProvider(Schemas.inMemory, inMemoryFileSystemProvider); - fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, inMemoryFileSystemProvider, Schemas.vscodeUserData, logService)); + const inMemoryFileSystemProvider = this._register(new InMemoryFileSystemProvider()); + this._register(fileService.registerProvider(Schemas.inMemory, inMemoryFileSystemProvider)); + this._register(fileService.registerProvider(Schemas.vscodeUserData, this._register(new FileUserDataProvider(Schemas.file, inMemoryFileSystemProvider, Schemas.vscodeUserData, logService)))); this.backupResourceJoiners = []; this.discardBackupJoiners = []; this.discardedBackups = []; this.pendingBackupsArr = []; this.discardedAllBackups = false; + + this._register(fileService); + this._register(lifecycleService); } testGetFileService(): IFileService { From db2ae1d7715da22b096db6332da1eb5a1a7f0939 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 7 Sep 2023 09:43:04 +0200 Subject: [PATCH 589/607] Error: Throttler is disposed in serve-web (fix #191624) --- src/vs/platform/files/node/diskFileSystemProviderServer.ts | 4 ++-- src/vs/platform/files/node/watcher/nodejs/nodejsClient.ts | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/files/node/diskFileSystemProviderServer.ts b/src/vs/platform/files/node/diskFileSystemProviderServer.ts index b7e81ab4491..fefc836be54 100644 --- a/src/vs/platform/files/node/diskFileSystemProviderServer.ts +++ b/src/vs/platform/files/node/diskFileSystemProviderServer.ts @@ -324,11 +324,11 @@ export abstract class AbstractSessionFileWatcher extends Disposable implements I } override dispose(): void { - super.dispose(); - for (const [, disposable] of this.watcherRequests) { disposable.dispose(); } this.watcherRequests.clear(); + + super.dispose(); } } diff --git a/src/vs/platform/files/node/watcher/nodejs/nodejsClient.ts b/src/vs/platform/files/node/watcher/nodejs/nodejsClient.ts index 49a38f7c4dc..b57721ef43c 100644 --- a/src/vs/platform/files/node/watcher/nodejs/nodejsClient.ts +++ b/src/vs/platform/files/node/watcher/nodejs/nodejsClient.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DisposableStore } from 'vs/base/common/lifecycle'; import { IDiskFileChange, ILogMessage, AbstractNonRecursiveWatcherClient, INonRecursiveWatcher } from 'vs/platform/files/common/watcher'; import { NodeJSWatcher } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcher'; @@ -18,7 +19,7 @@ export class NodeJSWatcherClient extends AbstractNonRecursiveWatcherClient { this.init(); } - protected override createWatcher(): INonRecursiveWatcher { - return new NodeJSWatcher(); + protected override createWatcher(disposables: DisposableStore): INonRecursiveWatcher { + return disposables.add(new NodeJSWatcher()); } } From b651f3399afad127688c65f59bbc0c0dc2128247 Mon Sep 17 00:00:00 2001 From: Hans Date: Thu, 7 Sep 2023 15:48:31 +0800 Subject: [PATCH 590/607] Adjust openview (#191907) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add firstFileUnresolved for openView * 💄 --- .../contrib/comments/browser/comments.contribution.ts | 4 ++-- .../workbench/contrib/comments/browser/commentsController.ts | 5 +++-- .../contrib/comments/common/commentsConfiguration.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts index 803aa80a18e..bec1b69199e 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts @@ -24,8 +24,8 @@ Registry.as(ConfigurationExtensions.Configuration).regis markdownDeprecationMessage: nls.localize('comments.openPanel.deprecated', "This setting is deprecated in favor of `comments.openView`.") }, 'comments.openView': { - enum: ['never', 'file', 'firstFile'], - enumDescriptions: [nls.localize('comments.openView.never', "The comments view will never be opened."), nls.localize('comments.openView.file', "The comments view will open when a file with comments is active."), nls.localize('comments.openView.firstFile', "If the comments view has not been opened yet during this session it will open the first time during a session that a file with comments is active.")], + enum: ['never', 'file', 'firstFile', 'firstFileUnresolved'], + enumDescriptions: [nls.localize('comments.openView.never', "The comments view will never be opened."), nls.localize('comments.openView.file', "The comments view will open when a file with comments is active."), nls.localize('comments.openView.firstFile', "If the comments view has not been opened yet during this session it will open the first time during a session that a file with comments is active."), nls.localize('comments.openView.firstFileUnresolved', "If the comments view has not been opened yet during this session and the comment is not resolved, it will open the first time during a session that a file with comments is active.")], default: 'firstFile', description: nls.localize('comments.openView', "Controls when the comments view should open."), restricted: false diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 9fac6af0a65..287d104802e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -727,9 +727,10 @@ export class CommentController implements IEditorContribution { private async openCommentsView(thread: languages.CommentThread) { if (thread.comments && (thread.comments.length > 0)) { - if (this.configurationService.getValue(COMMENTS_SECTION).openView === 'file') { + const openViewState = this.configurationService.getValue(COMMENTS_SECTION).openView; + if (openViewState === 'file') { return this.viewsService.openView(COMMENTS_VIEW_ID); - } else if (this.configurationService.getValue(COMMENTS_SECTION).openView === 'firstFile') { + } else if (openViewState === 'firstFile' || (openViewState === 'firstFileUnresolved' && thread.state === languages.CommentThreadState.Unresolved)) { const hasShownView = this.viewsService.getViewWithId(COMMENTS_VIEW_ID)?.hasRendered; if (!hasShownView) { return this.viewsService.openView(COMMENTS_VIEW_ID); diff --git a/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts b/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts index efc9d016415..c996a07373b 100644 --- a/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts +++ b/src/vs/workbench/contrib/comments/common/commentsConfiguration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ export interface ICommentsConfiguration { - openView: 'never' | 'file' | 'firstFile'; + openView: 'never' | 'file' | 'firstFile' | 'firstFileUnresolved'; useRelativeTime: boolean; visible: boolean; maxHeight: boolean; From c8024c841781b8a255f95a4eb5250bf3fc3d94c4 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Thu, 7 Sep 2023 11:59:35 +0200 Subject: [PATCH 591/607] fix floating scrollbar, remove hardcoded value --- src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 0772d57a3ce..460698f6749 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -26,8 +26,6 @@ interface IRenderedEditorLabel { export class NoTabsTitleControl extends TitleControl { - private static readonly HEIGHT = 35; - private titleContainer: HTMLElement | undefined; private editorLabel: IResourceLabel | undefined; private activeLabel: IRenderedEditorLabel = Object.create(null); @@ -352,7 +350,7 @@ export class NoTabsTitleControl extends TitleControl { getHeight(): IEditorGroupTitleHeight { return { - total: NoTabsTitleControl.HEIGHT, + total: this.titleHeight, offset: 0 }; } From ed0d3d50f1118621da5ca0adda0fff499f9aff2d Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 7 Sep 2023 12:58:16 +0200 Subject: [PATCH 592/607] a bit more `ensureNoDisposablesAreLeakedInTestSuite` work --- .../browser/services/openerService.test.ts | 21 +++++++++++-------- .../test/common/uriIdentityService.test.ts | 7 +++++++ .../test/node/commonProperties.test.ts | 3 +++ 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/vs/editor/test/browser/services/openerService.test.ts b/src/vs/editor/test/browser/services/openerService.test.ts index d823f89d285..4f1b579d5e7 100644 --- a/src/vs/editor/test/browser/services/openerService.test.ts +++ b/src/vs/editor/test/browser/services/openerService.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; @@ -33,6 +34,8 @@ suite('OpenerService', function () { lastCommand = undefined; }); + const store = ensureNoDisposablesAreLeakedInTestSuite(); + test('delegate to editorService, scheme:///fff', async function () { const openerService = new OpenerService(editorService, NullCommandService); await openerService.open(URI.parse('another:///somepath')); @@ -83,7 +86,7 @@ suite('OpenerService', function () { const openerService = new OpenerService(editorService, commandService); const id = `aCommand${Math.random()}`; - CommandsRegistry.registerCommand(id, function () { }); + store.add(CommandsRegistry.registerCommand(id, function () { })); assert.strictEqual(lastCommand, undefined); await openerService.open(URI.parse('command:' + id)); @@ -91,11 +94,11 @@ suite('OpenerService', function () { }); - test('delegate to commandsService, command:someid', async function () { + test('delegate to commandsService, command:someid, 2', async function () { const openerService = new OpenerService(editorService, commandService); const id = `aCommand${Math.random()}`; - CommandsRegistry.registerCommand(id, function () { }); + store.add(CommandsRegistry.registerCommand(id, function () { })); await openerService.open(URI.parse('command:' + id).with({ query: '\"123\"' }), { allowCommands: true }); assert.strictEqual(lastCommand!.id, id); @@ -121,7 +124,7 @@ suite('OpenerService', function () { test('links are protected by validators', async function () { const openerService = new OpenerService(editorService, commandService); - openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) }); + store.add(openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) })); const httpResult = await openerService.open(URI.parse('https://www.microsoft.com')); const httpsResult = await openerService.open(URI.parse('https://www.microsoft.com')); @@ -132,15 +135,15 @@ suite('OpenerService', function () { test('links validated by validators go to openers', async function () { const openerService = new OpenerService(editorService, commandService); - openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) }); + store.add(openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) })); let openCount = 0; - openerService.registerOpener({ + store.add(openerService.registerOpener({ open: (resource: URI) => { openCount++; return Promise.resolve(true); } - }); + })); await openerService.open(URI.parse('http://microsoft.com')); assert.strictEqual(openCount, 1); @@ -151,13 +154,13 @@ suite('OpenerService', function () { test('links aren\'t manipulated before being passed to validator: PR #118226', async function () { const openerService = new OpenerService(editorService, commandService); - openerService.registerValidator({ + store.add(openerService.registerValidator({ shouldOpen: (resource) => { // We don't want it to convert strings into URIs assert.strictEqual(resource instanceof URI, false); return Promise.resolve(false); } - }); + })); await openerService.open('https://wwww.microsoft.com'); await openerService.open('https://www.microsoft.com??params=CountryCode%3DUSA%26Name%3Dvscode"'); }); diff --git a/src/vs/platform/uriIdentity/test/common/uriIdentityService.test.ts b/src/vs/platform/uriIdentity/test/common/uriIdentityService.test.ts index caea0117d08..4b20f54a4db 100644 --- a/src/vs/platform/uriIdentity/test/common/uriIdentityService.test.ts +++ b/src/vs/platform/uriIdentity/test/common/uriIdentityService.test.ts @@ -9,6 +9,7 @@ import { mock } from 'vs/base/test/common/mock'; import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('URI Identity', function () { @@ -38,6 +39,12 @@ suite('URI Identity', function () { ]))); }); + teardown(function () { + _service.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); + function assertCanonical(input: URI, expected: URI, service: UriIdentityService = _service) { const actual = service.asCanonicalUri(input); assert.strictEqual(actual.toString(), expected.toString()); diff --git a/src/vs/workbench/services/telemetry/test/node/commonProperties.test.ts b/src/vs/workbench/services/telemetry/test/node/commonProperties.test.ts index 8759a80f67d..8600bd13f15 100644 --- a/src/vs/workbench/services/telemetry/test/node/commonProperties.test.ts +++ b/src/vs/workbench/services/telemetry/test/node/commonProperties.test.ts @@ -8,6 +8,7 @@ import { release, hostname } from 'os'; import { resolveWorkbenchCommonProperties } from 'vs/workbench/services/telemetry/common/workbenchCommonProperties'; import { IStorageService, StorageScope, InMemoryStorageService, StorageTarget } from 'vs/platform/storage/common/storage'; import { timeout } from 'vs/base/common/async'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Telemetry - common properties', function () { const commit: string = (undefined)!; @@ -18,6 +19,8 @@ suite('Telemetry - common properties', function () { testStorageService = new InMemoryStorageService(); }); + ensureNoDisposablesAreLeakedInTestSuite(); + test('default', function () { const props = resolveWorkbenchCommonProperties(testStorageService, release(), hostname(), commit, version, 'someMachineId', false, process); assert.ok('commitHash' in props); From ab7b0c6db626549c458fec406b16cb746b891c59 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 7 Sep 2023 05:16:29 -0700 Subject: [PATCH 593/607] Remove unwanted code --- .../test/browser/xterm/xtermTerminal.test.ts | 42 +------------------ 1 file changed, 2 insertions(+), 40 deletions(-) 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 d31face6263..87287f0664c 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 @@ -33,9 +33,7 @@ import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKe import { Color, RGBA } from 'vs/base/common/color'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITerminalLogService } from 'vs/platform/terminal/common/terminal'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import type { Suite } from 'mocha'; // eslint-disable-line local/code-import-patterns class TestWebglAddon implements WebglAddon { static shouldThrow = false; @@ -94,45 +92,9 @@ const defaultTerminalConfig: Partial = { unicodeVersion: '6' }; +suite('XtermTerminal', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); -interface NoLeakSuiteFunction { - (title: string, fn: (this: Suite, store: Pick) => void): Suite; - only(title: string, fn: (this: Suite, store: Pick) => void): Suite; - skip(title: string, fn: (this: Suite, store: Pick) => void): Suite | void; -} -function noLeakSuiteBaseFn(that: Suite, fn: (this: Suite, store: Pick) => void): void { - let store: DisposableStore; - // Wrap store as the suite function is called before it's initialized - const testContext = { - add(o: T): T { - return store.add(o); - } - }; - setup(() => store = new DisposableStore()); - teardown(() => store.dispose()); - ensureNoDisposablesAreLeakedInTestSuite(); - fn.bind(that)(testContext); -} -function noLeakSuiteFn(title: string, fn: (this: Suite, store: Pick) => void): Suite { - return suite(title, function () { - noLeakSuiteBaseFn(this, fn); - }); -} -noLeakSuiteFn.only = (title: string, fn: (this: Suite, store: Pick) => void): Suite => { - return suite.only(title, function () { // eslint-disable-line local/code-no-test-only - noLeakSuiteBaseFn(this, fn); - }); -}; -noLeakSuiteFn.skip = (title: string, fn: (this: Suite, store: Pick) => void): Suite | void => { - return suite.skip(title, function () { - noLeakSuiteBaseFn(this, fn); - }); -}; -const noLeakSuite: NoLeakSuiteFunction = noLeakSuiteFn; - - - -noLeakSuite('XtermTerminal', store => { let instantiationService: TestInstantiationService; let configurationService: TestConfigurationService; let themeService: TestThemeService; From 4a6e5c124d2d541afcc363551459d252ae54fb5a Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 7 Sep 2023 14:16:36 +0200 Subject: [PATCH 594/607] update notebook milesones --- .vscode/notebooks/api.github-issues | 2 +- .vscode/notebooks/my-work.github-issues | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 706cb4d5d38..3c234735491 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"August 2023\"" + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"September 2023\"" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index 06efd345018..e48617f8fde 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"August 2023\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-unpkg repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github repo:microsoft/monaco-editor repo:microsoft/vscode-vsce repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-l10n repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release\n\n// current milestone name\n$milestone=milestone:\"September 2023\"" }, { "kind": 1, From df9171e630222778149cf1cc1edf3e7fd66ad124 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 7 Sep 2023 14:36:47 +0200 Subject: [PATCH 595/607] `vscode.TreeView.dispose` doesn't clean up in the renderer (#192403) Fixes #192126 --- .../api/browser/mainThreadTreeViews.ts | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 279a4f05c28..5451d23f352 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, CheckboxUpdate } from 'vs/workbench/api/common/extHost.protocol'; import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, ResolvableTreeItem, ITreeViewDragAndDropController, IViewBadge, NoTreeViewError } from 'vs/workbench/common/views'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; @@ -24,7 +24,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape { private readonly _proxy: ExtHostTreeViewsShape; - private readonly _dataProviders: Map = new Map(); + private readonly _dataProviders: Map = new Map(); private readonly _dndControllers = new Map(); constructor( @@ -43,7 +43,9 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this.extensionService.whenInstalledExtensionsRegistered().then(() => { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); - this._dataProviders.set(treeViewId, dataProvider); + const disposables = new DisposableStore(); + this._register(disposables); + this._dataProviders.set(treeViewId, { dataProvider, disposables }); const dndController = (options.hasHandleDrag || options.hasHandleDrop) ? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, this._proxy) : undefined; const viewer = this.getTreeView(treeViewId); @@ -58,7 +60,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._dndControllers.set(treeViewId, dndController); } viewer.dataProvider = dataProvider; - this.registerListeners(treeViewId, viewer); + this.registerListeners(treeViewId, viewer, disposables); this._proxy.$setVisible(treeViewId, viewer.visible); } else { this.notificationService.error('No view is registered with id: ' + treeViewId); @@ -73,7 +75,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie .then(() => { const viewer = this.getTreeView(treeViewId); if (viewer && itemInfo) { - return this.reveal(viewer, this._dataProviders.get(treeViewId)!, itemInfo.item, itemInfo.parentChain, options); + return this.reveal(viewer, this._dataProviders.get(treeViewId)!.dataProvider, itemInfo.item, itemInfo.parentChain, options); } return undefined; }); @@ -85,7 +87,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie const viewer = this.getTreeView(treeViewId); const dataProvider = this._dataProviders.get(treeViewId); if (viewer && dataProvider) { - const itemsToRefresh = dataProvider.getItemsToRefresh(itemsToRefreshByHandle); + const itemsToRefresh = dataProvider.dataProvider.getItemsToRefresh(itemsToRefreshByHandle); return viewer.refresh(itemsToRefresh.length ? itemsToRefresh : undefined); } return Promise.resolve(); @@ -132,6 +134,11 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie if (viewer) { viewer.dataProvider = undefined; } + const dataProvider = this._dataProviders.get(treeViewId); + if (dataProvider) { + dataProvider.disposables.dispose(); + this._dataProviders.delete(treeViewId); + } } private async reveal(treeView: ITreeView, dataProvider: TreeViewDataProvider, itemIn: ITreeItem, parentChain: ITreeItem[], options: IRevealOptions): Promise { @@ -175,12 +182,12 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie } } - private registerListeners(treeViewId: string, treeView: ITreeView): void { - this._register(treeView.onDidExpandItem(item => this._proxy.$setExpanded(treeViewId, item.handle, true))); - this._register(treeView.onDidCollapseItem(item => this._proxy.$setExpanded(treeViewId, item.handle, false))); - this._register(treeView.onDidChangeSelectionAndFocus(items => this._proxy.$setSelectionAndFocus(treeViewId, items.selection.map(({ handle }) => handle), items.focus.handle))); - this._register(treeView.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible))); - this._register(treeView.onDidChangeCheckboxState(items => { + private registerListeners(treeViewId: string, treeView: ITreeView, disposables: DisposableStore): void { + disposables.add(treeView.onDidExpandItem(item => this._proxy.$setExpanded(treeViewId, item.handle, true))); + disposables.add(treeView.onDidCollapseItem(item => this._proxy.$setExpanded(treeViewId, item.handle, false))); + disposables.add(treeView.onDidChangeSelectionAndFocus(items => this._proxy.$setSelectionAndFocus(treeViewId, items.selection.map(({ handle }) => handle), items.focus.handle))); + disposables.add(treeView.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible))); + disposables.add(treeView.onDidChangeCheckboxState(items => { this._proxy.$changeCheckboxState(treeViewId, items.map(item => { return { treeItemHandle: item.handle, newState: item.checkbox?.isChecked ?? false }; })); From 15094ed7fded9f2de45488c13b97a8fe64a596ed Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 7 Sep 2023 14:45:58 +0200 Subject: [PATCH 596/607] Cannot read properties of null (reading 'uri') (#192404) Fixes #192331 --- src/vs/workbench/contrib/comments/browser/commentsController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 287d104802e..de414ee854f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -741,7 +741,7 @@ export class CommentController implements IEditorContribution { } private displayCommentThread(owner: string, thread: languages.CommentThread, pendingComment: string | undefined, pendingEdits: { [key: number]: string } | undefined): void { - if (!this.editor) { + if (!this.editor?.getModel()) { return; } if (this.isEditorInlineOriginal(this.editor)) { From 0989cb2a6c308de70b0ef9ad5c8756081b075857 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 7 Sep 2023 06:07:20 -0700 Subject: [PATCH 597/607] Fix leak in test --- .../contrib/terminal/test/browser/xterm/xtermTerminal.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 87287f0664c..c43605db7b4 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 @@ -123,7 +123,7 @@ suite('XtermTerminal', () => { instantiationService.stub(IThemeService, themeService); instantiationService.stub(IViewDescriptorService, viewDescriptorService); instantiationService.stub(IContextMenuService, store.add(instantiationService.createInstance(ContextMenuService))); - instantiationService.stub(ILifecycleService, new TestLifecycleService()); + instantiationService.stub(ILifecycleService, store.add(new TestLifecycleService())); instantiationService.stub(IContextKeyService, new MockContextKeyService()); configHelper = store.add(instantiationService.createInstance(TerminalConfigHelper)); From afdf655f975bcdbec711ac8870d638ee0309e791 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Thu, 7 Sep 2023 15:20:00 +0200 Subject: [PATCH 598/607] Refactors diff algorithm --- .../algorithms/myersDiffAlgorithm.ts | 29 ++++++++++++------- ...dget2.test.ts => diffEditorWidget.test.ts} | 0 ...st.ts => defaultLinesDiffComputer.test.ts} | 20 +++++++++---- ...iffingFixture.test.ts => fixtures.test.ts} | 2 +- 4 files changed, 34 insertions(+), 17 deletions(-) rename src/vs/editor/test/browser/widget/{diffEditorWidget2.test.ts => diffEditorWidget.test.ts} (100%) rename src/vs/editor/test/node/diffing/{advancedLinesDiffComputer.test.ts => defaultLinesDiffComputer.test.ts} (81%) rename src/vs/editor/test/node/diffing/{diffingFixture.test.ts => fixtures.test.ts} (99%) diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts index 0f3b64004cd..c0843188927 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts @@ -17,8 +17,11 @@ export class MyersDiffAlgorithm implements IDiffAlgorithm { return DiffAlgorithmResult.trivial(seq1, seq2); } + const seqX = seq1; // Text on the x axis + const seqY = seq2; // Text on the y axis + function getXAfterSnake(x: number, y: number): number { - while (x < seq1.length && y < seq2.length && seq1.getElement(x) === seq2.getElement(y)) { + while (x < seqX.length && y < seqY.length && seqX.getElement(x) === seqY.getElement(y)) { x++; y++; } @@ -29,6 +32,7 @@ export class MyersDiffAlgorithm implements IDiffAlgorithm { // V[k]: X value of longest d-line that ends in diagonal k. // d-line: path from (0,0) to (x,y) that uses exactly d non-diagonals. // diagonal k: Set of points (x,y) with x-y = k. + // k=1 -> (1,0),(2,1) const V = new FastInt32Array(); V.set(0, getXAfterSnake(0, 0)); @@ -40,18 +44,21 @@ export class MyersDiffAlgorithm implements IDiffAlgorithm { loop: while (true) { d++; if (!timeout.isValid()) { - return DiffAlgorithmResult.trivialTimedOut(seq1, seq2); + return DiffAlgorithmResult.trivialTimedOut(seqX, seqY); } // The paper has `for (k = -d; k <= d; k += 2)`, but we can ignore diagonals that cannot influence the result. - const lowerBound = -Math.min(d, seq2.length + (d % 2)); - const upperBound = Math.min(d, seq1.length + (d % 2)); + const lowerBound = -Math.min(d, seqY.length + (d % 2)); + const upperBound = Math.min(d, seqX.length + (d % 2)); for (k = lowerBound; k <= upperBound; k += 2) { + let step = 0; // We can use the X values of (d-1)-lines to compute X value of the longest d-lines. - const maxXofDLineTop = k === upperBound ? -1 : V.get(k + 1); // We take a vertical non-diagonal (add a symbol in seq1) - const maxXofDLineLeft = k === lowerBound ? -1 : V.get(k - 1) + 1; // We take a horizontal non-diagonal (+1 x) (delete a symbol in seq1) - const x = Math.min(Math.max(maxXofDLineTop, maxXofDLineLeft), seq1.length); + const maxXofDLineTop = k === upperBound ? -1 : V.get(k + 1); // We take a vertical non-diagonal (add a symbol in seqX) + const maxXofDLineLeft = k === lowerBound ? -1 : V.get(k - 1) + 1; // We take a horizontal non-diagonal (+1 x) (delete a symbol in seqX) + step++; + const x = Math.min(Math.max(maxXofDLineTop, maxXofDLineLeft), seqX.length); const y = x - k; - if (x > seq1.length || y > seq2.length) { + step++; + if (x > seqX.length || y > seqY.length) { // This diagonal is irrelevant for the result. // TODO: Don't pay the cost for this in the next iteration. continue; @@ -61,7 +68,7 @@ export class MyersDiffAlgorithm implements IDiffAlgorithm { const lastPath = x === maxXofDLineTop ? paths.get(k + 1) : paths.get(k - 1); paths.set(k, newMaxX !== x ? new SnakePath(lastPath, x, y, newMaxX - x) : lastPath); - if (V.get(k) === seq1.length && V.get(k) - k === seq2.length) { + if (V.get(k) === seqX.length && V.get(k) - k === seqY.length) { break loop; } } @@ -69,8 +76,8 @@ export class MyersDiffAlgorithm implements IDiffAlgorithm { let path = paths.get(k); const result: SequenceDiff[] = []; - let lastAligningPosS1: number = seq1.length; - let lastAligningPosS2: number = seq2.length; + let lastAligningPosS1: number = seqX.length; + let lastAligningPosS2: number = seqY.length; while (true) { const endX = path ? path.x + path.length : 0; diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts similarity index 100% rename from src/vs/editor/test/browser/widget/diffEditorWidget2.test.ts rename to src/vs/editor/test/browser/widget/diffEditorWidget.test.ts diff --git a/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts similarity index 81% rename from src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts rename to src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts index 31fa3e3086e..664ef33cf4f 100644 --- a/src/vs/editor/test/node/diffing/advancedLinesDiffComputer.test.ts +++ b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts @@ -6,12 +6,24 @@ import * as assert from 'assert'; import { Range } from 'vs/editor/common/core/range'; import { RangeMapping } from 'vs/editor/common/diff/rangeMapping'; -import { getLineRangeMapping } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { OffsetRange } from 'vs/editor/common/core/offsetRange'; +import { getLineRangeMapping } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; +import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; +import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing'; + +suite('myers', () => { + test('1', () => { + const s1 = new LinesSliceCharSequence(['hello world'], new OffsetRange(0, 1), true); + const s2 = new LinesSliceCharSequence(['hallo welt'], new OffsetRange(0, 1), true); + + const a = true ? new MyersDiffAlgorithm() : new DynamicProgrammingDiffing(); + a.compute(s1, s2); + }); +}); suite('lineRangeMapping', () => { - test('1', () => { + test('Simple', () => { assert.deepStrictEqual( getLineRangeMapping( new RangeMapping( @@ -32,7 +44,7 @@ suite('lineRangeMapping', () => { ); }); - test('2', () => { + test('Empty Lines', () => { assert.deepStrictEqual( getLineRangeMapping( new RangeMapping( @@ -56,8 +68,6 @@ suite('lineRangeMapping', () => { }); suite('LinesSliceCharSequence', () => { - // Create tests for translateOffset - const sequence = new LinesSliceCharSequence( [ 'line1: foo', diff --git a/src/vs/editor/test/node/diffing/diffingFixture.test.ts b/src/vs/editor/test/node/diffing/fixtures.test.ts similarity index 99% rename from src/vs/editor/test/node/diffing/diffingFixture.test.ts rename to src/vs/editor/test/node/diffing/fixtures.test.ts index bb42c69d62c..a7ff5dbe5a1 100644 --- a/src/vs/editor/test/node/diffing/diffingFixture.test.ts +++ b/src/vs/editor/test/node/diffing/fixtures.test.ts @@ -12,7 +12,7 @@ import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; -suite('diff fixtures', () => { +suite('diffing fixtures', () => { setup(() => { setUnexpectedErrorHandler(e => { throw e; From 3551954b33c5e93c9ebd09b77e9e92680609b9d0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 7 Sep 2023 06:44:03 -0700 Subject: [PATCH 599/607] Fix leak in Buffer Content Tracker test --- .../accessibility/test/browser/bufferContentTracker.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts index d494ce0a93d..b250ab72630 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/test/browser/bufferContentTracker.test.ts @@ -63,7 +63,7 @@ suite('Buffer Content Tracker', () => { instantiationService.stub(ITerminalLogService, new NullLogService()); instantiationService.stub(ILoggerService, store.add(new TestLoggerService())); instantiationService.stub(IContextMenuService, store.add(instantiationService.createInstance(ContextMenuService))); - instantiationService.stub(ILifecycleService, new TestLifecycleService()); + instantiationService.stub(ILifecycleService, store.add(new TestLifecycleService())); instantiationService.stub(IContextKeyService, new MockContextKeyService()); configHelper = store.add(instantiationService.createInstance(TerminalConfigHelper)); capabilities = store.add(new TerminalCapabilityStore()); From ae8de5902b47c2a04f9d56cc3fc3b0df827c79ef Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 7 Sep 2023 16:02:20 +0200 Subject: [PATCH 600/607] add logging when updating token (#192412) --- .../browser/userDataSyncWorkbenchService.ts | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index 71cbb86e4d7..e5587cb3cda 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -175,15 +175,13 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat this._register(this.authenticationService.onDidChangeDeclaredProviders(() => this.updateAuthenticationProviders())); - this._register( + this._register(Event.filter( Event.any( - Event.filter( - Event.any( - this.authenticationService.onDidRegisterAuthenticationProvider, - this.authenticationService.onDidUnregisterAuthenticationProvider, - ), info => this.isSupportedAuthenticationProviderId(info.id)), - Event.filter(this.userDataSyncAccountService.onTokenFailed, isSuccessive => !isSuccessive)) - (() => this.update())); + this.authenticationService.onDidRegisterAuthenticationProvider, + this.authenticationService.onDidUnregisterAuthenticationProvider, + ), info => this.isSupportedAuthenticationProviderId(info.id))(() => this.update())); + + this._register(Event.filter(this.userDataSyncAccountService.onTokenFailed, isSuccessive => !isSuccessive)(() => this.update('token failure'))); this._register(Event.filter(this.authenticationService.onDidChangeSessions, e => this.isSupportedAuthenticationProviderId(e.providerId))(({ event }) => this.onDidChangeSessions(event))); this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, this._register(new DisposableStore()))(() => this.onDidChangeStorage())); @@ -205,7 +203,11 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat })); } - private async update(): Promise { + private async update(reason?: string): Promise { + + if (reason) { + this.logService.info(`Settings Sync: Updating due to ${reason}`); + } this.updateAuthenticationProviders(); await this.updateCurrentAccount(); @@ -611,20 +613,20 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat private async onDidAuthFailure(): Promise { this.telemetryService.publicLog2<{}, { owner: 'sandy081'; comment: 'Report when there are successive auth failures during settings sync' }>('sync/successiveAuthFailures'); this.currentSessionId = undefined; - await this.update(); + await this.update('auth failure'); } private onDidChangeSessions(e: AuthenticationSessionsChangeEvent): void { if (this.currentSessionId && e.removed.find(session => session.id === this.currentSessionId)) { this.currentSessionId = undefined; } - this.update(); + this.update('change in sessions'); } private onDidChangeStorage(): void { if (this.currentSessionId !== this.getStoredCachedSessionId() /* This checks if current window changed the value or not */) { this._cachedCurrentSessionId = null; - this.update(); + this.update('change in storage'); } } From 242d96340a4220788ff03afbef5516b09c17155b Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 7 Sep 2023 16:03:20 +0200 Subject: [PATCH 601/607] debt - dispose things in tests (#192392) * debt - ensure more dispose from tests * more * input * . * more * input * inp * fux --- src/vs/base/common/async.ts | 17 +- src/vs/platform/state/node/stateService.ts | 13 +- src/vs/platform/state/test/node/state.test.ts | 39 ++-- .../test/common/storageService.test.ts | 12 +- .../electron-main/storageMainService.test.ts | 2 +- .../browser/parts/editor/titleControl.ts | 1 + .../browser/parts/statusbar/statusbarModel.ts | 1 - .../test/browser/saveParticipant.test.ts | 21 +- .../workbench/contrib/files/common/files.ts | 11 +- .../test/browser/fileEditorInput.test.ts | 93 ++++----- .../test/browser/fileOnDiskProvider.test.ts | 12 +- .../preferences/browser/keybindingWidgets.ts | 2 +- .../test/browser/editorsObserver.test.ts | 115 ++++++----- .../test/browser/historyService.test.ts | 140 ++++++++----- .../languageDetectionWorkerServiceImpl.ts | 4 +- .../electron-sandbox/lifecycleService.test.ts | 37 ++-- .../test/browser/arrayOperation.test.ts | 0 .../test/browser/textEditorService.test.ts | 85 ++++---- .../test/browser/textFileService.test.ts | 69 +++---- .../nativeTextFileService.io.test.ts | 3 + .../browser/textModelResolverService.test.ts | 31 ++- .../test/browser/untitledTextEditor.test.ts | 191 +++++++----------- .../browser/workingCopyBackupTracker.test.ts | 23 +-- .../browser/workingCopyFileService.test.ts | 48 ++--- src/vs/workbench/test/browser/part.test.ts | 23 ++- .../parts/editor/diffEditorInput.test.ts | 40 ++-- .../parts/editor/editorDiffModel.test.ts | 21 +- .../parts/editor/editorGroupModel.test.ts | 103 +++++----- .../browser/parts/editor/editorInput.test.ts | 14 +- .../browser/parts/editor/editorModel.test.ts | 27 ++- .../parts/editor/resourceEditorInput.test.ts | 5 +- .../editor/sideBySideEditorInput.test.ts | 43 ++-- .../editor/textResourceEditorInput.test.ts | 29 +-- .../parts/statusbar/statusbarModel.test.ts | 28 ++- src/vs/workbench/test/browser/viewlet.test.ts | 3 + src/vs/workbench/test/common/memento.test.ts | 13 +- .../test/common/notifications.test.ts | 93 +++++---- .../workbench/test/common/resources.test.ts | 14 +- 38 files changed, 757 insertions(+), 669 deletions(-) rename src/vs/workbench/{ => services/textMate}/test/browser/arrayOperation.test.ts (100%) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 152a6af7e4e..95d17f691d8 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -6,7 +6,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { CancellationError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableMap, IDisposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { extUri as defaultExtUri, IExtUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { setTimeout0 } from 'vs/base/common/platform'; @@ -717,6 +717,9 @@ export class ResourceQueue implements IDisposable { private readonly drainers = new Set>(); + private drainListeners: DisposableMap | undefined = undefined; + private drainListenerCount = 0; + async whenDrained(): Promise { if (this.isDrained()) { return; @@ -744,12 +747,20 @@ export class ResourceQueue implements IDisposable { let queue = this.queues.get(key); if (!queue) { queue = new Queue(); - Event.once(queue.onDrained)(() => { + const drainListenerId = this.drainListenerCount++; + const drainListener = Event.once(queue.onDrained)(() => { queue?.dispose(); this.queues.delete(key); this.onDidQueueDrain(); + + this.drainListeners?.deleteAndDispose(drainListenerId); }); + if (!this.drainListeners) { + this.drainListeners = new DisposableMap(); + } + this.drainListeners.set(drainListenerId, drainListener); + this.queues.set(key, queue); } @@ -786,6 +797,8 @@ export class ResourceQueue implements IDisposable { // promises when the resource queue is being // disposed. this.releaseDrainers(); + + this.drainListeners?.dispose(); } } diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index ebe9318124a..debf184e7d4 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -5,6 +5,7 @@ import { ThrottledDelayer } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; +import { Disposable } from 'vs/base/common/lifecycle'; import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -19,7 +20,7 @@ export const enum SaveStrategy { DELAYED } -export class FileStorage { +export class FileStorage extends Disposable { private storage: StorageDatabase = Object.create(null); private lastSavedStorageContents = ''; @@ -35,7 +36,9 @@ export class FileStorage { private readonly logService: ILogService, private readonly fileService: IFileService, ) { - this.flushDelayer = saveStrategy === SaveStrategy.IMMEDIATE ? undefined : new ThrottledDelayer(100 /* buffer saves over a short time */); + super(); + + this.flushDelayer = saveStrategy === SaveStrategy.IMMEDIATE ? undefined : this._register(new ThrottledDelayer(100 /* buffer saves over a short time */)); } init(): Promise { @@ -157,7 +160,7 @@ export class FileStorage { } } -export class StateReadonlyService implements IStateReadService { +export class StateReadonlyService extends Disposable implements IStateReadService { declare readonly _serviceBrand: undefined; @@ -169,7 +172,9 @@ export class StateReadonlyService implements IStateReadService { @ILogService logService: ILogService, @IFileService fileService: IFileService ) { - this.fileStorage = new FileStorage(environmentService.stateResource, saveStrategy, logService, fileService); + super(); + + this.fileStorage = this._register(new FileStorage(environmentService.stateResource, saveStrategy, logService, fileService)); } async init(): Promise { diff --git a/src/vs/platform/state/test/node/state.test.ts b/src/vs/platform/state/test/node/state.test.ts index af5d0781dd6..c77c3a86e3f 100644 --- a/src/vs/platform/state/test/node/state.test.ts +++ b/src/vs/platform/state/test/node/state.test.ts @@ -6,10 +6,12 @@ import * as assert from 'assert'; import { readFileSync } from 'fs'; import { tmpdir } from 'os'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; import { Promises, writeFileSync } from 'vs/base/node/pfs'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; import { IFileService } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; @@ -24,21 +26,22 @@ flakySuite('StateService', () => { let logService: ILogService; let diskFileSystemProvider: DiskFileSystemProvider; + const disposables = new DisposableStore(); + setup(() => { testDir = getRandomTestPath(tmpdir(), 'vsctests', 'statemainservice'); logService = new NullLogService(); - fileService = new FileService(logService); - diskFileSystemProvider = new DiskFileSystemProvider(logService); - fileService.registerProvider(Schemas.file, diskFileSystemProvider); + fileService = disposables.add(new FileService(logService)); + diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(logService)); + disposables.add(fileService.registerProvider(Schemas.file, diskFileSystemProvider)); return Promises.mkdir(testDir, { recursive: true }); }); teardown(() => { - fileService.dispose(); - diskFileSystemProvider.dispose(); + disposables.clear(); return Promises.rm(testDir); }); @@ -47,7 +50,7 @@ flakySuite('StateService', () => { const storageFile = join(testDir, 'storage.json'); writeFileSync(storageFile, ''); - let service = new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService); + let service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService)); await service.init(); service.setItem('some.key', 'some.value'); @@ -62,7 +65,7 @@ flakySuite('StateService', () => { await service.close(); - service = new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService); + service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService)); await service.init(); assert.strictEqual(service.getItem('some.other.key'), 'some.other.value'); @@ -103,13 +106,15 @@ flakySuite('StateService', () => { assert.strictEqual(service.getItem('some.setItems.key3'), undefined); assert.strictEqual(service.getItem('some.setItems.key4'), undefined); assert.strictEqual(service.getItem('some.setItems.key5'), undefined); + + return service.close(); }); test('Basics (immediate strategy)', async function () { const storageFile = join(testDir, 'storage.json'); writeFileSync(storageFile, ''); - let service = new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService); + let service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService)); await service.init(); service.setItem('some.key', 'some.value'); @@ -124,7 +129,7 @@ flakySuite('StateService', () => { await service.close(); - service = new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService); + service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService)); await service.init(); assert.strictEqual(service.getItem('some.other.key'), 'some.other.value'); @@ -171,7 +176,7 @@ flakySuite('StateService', () => { const storageFile = join(testDir, 'storage.json'); writeFileSync(storageFile, ''); - let service = new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService); + let service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService)); await service.init(); service.setItem('some.key1', 'some.value1'); @@ -187,7 +192,7 @@ flakySuite('StateService', () => { await service.close(); - service = new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService); + service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService)); await service.init(); assert.strictEqual(service.getItem('some.key1'), 'some.value1'); @@ -200,7 +205,7 @@ flakySuite('StateService', () => { const storageFile = join(testDir, 'storage.json'); writeFileSync(storageFile, ''); - let service = new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService); + let service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService)); await service.init(); service.setItem('some.key1', 'some.value1'); @@ -216,7 +221,7 @@ flakySuite('StateService', () => { await service.close(); - service = new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService); + service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.IMMEDIATE, logService, fileService)); await service.init(); assert.strictEqual(service.getItem('some.key1'), 'some.value1'); @@ -229,7 +234,7 @@ flakySuite('StateService', () => { const storageFile = join(testDir, 'storage.json'); writeFileSync(storageFile, ''); - const service = new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService); + const service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService)); service.setItem('some.key1', 'some.value1'); service.setItem('some.key2', 'some.value2'); @@ -254,7 +259,7 @@ flakySuite('StateService', () => { const storageFile = join(testDir, 'storage.json'); writeFileSync(storageFile, ''); - const service = new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService); + const service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService)); await service.init(); @@ -278,7 +283,7 @@ flakySuite('StateService', () => { const storageFile = join(testDir, 'storage.json'); writeFileSync(storageFile, ''); - const service = new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService); + const service = disposables.add(new FileStorage(URI.file(storageFile), SaveStrategy.DELAYED, logService, fileService)); service.setItem('some.key1', 'some.value1'); service.setItem('some.key2', 'some.value2'); @@ -290,4 +295,6 @@ flakySuite('StateService', () => { const contents = readFileSync(storageFile).toString(); assert.strictEqual(contents.length, 0); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index 93ac0d93721..d781cf027e6 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -5,6 +5,7 @@ import { deepStrictEqual, ok, strictEqual } from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { InMemoryStorageService, IStorageService, IStorageTargetChangeEvent, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; export function createSuite(params: { setup: () => Promise; teardown: (service: T) => Promise }): void { @@ -284,8 +285,17 @@ export function createSuite(params: { setup: () => Pr } suite('StorageService (in-memory)', function () { + + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + createSuite({ - setup: async () => new InMemoryStorageService(), + setup: async () => disposables.add(new InMemoryStorageService()), teardown: async () => { } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); 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 f3467af580f..1455f234f7b 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -115,7 +115,7 @@ suite('StorageMainService', function () { const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); const fileService = disposables.add(new FileService(new NullLogService())); const uriIdentityService = disposables.add(new UriIdentityService(fileService)); - const testStorageService = disposables.add(new TestStorageMainService(new NullLogService(), environmentService, disposables.add(new UserDataProfilesMainService(new StateService(SaveStrategy.DELAYED, environmentService, new NullLogService(), fileService), disposables.add(uriIdentityService), environmentService, fileService, new NullLogService())), lifecycleMainService, fileService, uriIdentityService)); + const testStorageService = disposables.add(new TestStorageMainService(new NullLogService(), environmentService, disposables.add(new UserDataProfilesMainService(disposables.add(new StateService(SaveStrategy.DELAYED, environmentService, new NullLogService(), fileService)), disposables.add(uriIdentityService), environmentService, fileService, new NullLogService())), lifecycleMainService, fileService, uriIdentityService)); disposables.add(testStorageService.applicationStorage); diff --git a/src/vs/workbench/browser/parts/editor/titleControl.ts b/src/vs/workbench/browser/parts/editor/titleControl.ts index 22187e0daee..d09cbb7008b 100644 --- a/src/vs/workbench/browser/parts/editor/titleControl.ts +++ b/src/vs/workbench/browser/parts/editor/titleControl.ts @@ -438,6 +438,7 @@ export abstract class TitleControl extends Themable { } updateOptions(oldOptions: IEditorPartOptions, newOptions: IEditorPartOptions): void { + // Update title height if (oldOptions.tabHeight !== newOptions.tabHeight) { this.updateTitleHeight(); diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts index eff2421efd0..8e943eec515 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts @@ -41,7 +41,6 @@ export class StatusbarViewModel extends Disposable { this.restoreState(); this.registerListeners(); - } private restoreState(): void { diff --git a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts index 9c2bf728cef..f8960304793 100644 --- a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts @@ -8,7 +8,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { FinalNewLineParticipant, TrimFinalNewLinesParticipant, TrimWhitespaceParticipant } from 'vs/workbench/contrib/codeEditor/browser/saveParticipants'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench/test/browser/workbenchTestServices'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; @@ -19,23 +19,22 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Save Participants', function () { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); + disposables.add(accessor.textFileService.files); }); teardown(() => { - (accessor.textFileService.files).dispose(); - disposables.dispose(); + disposables.clear(); }); test('insert final new line', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; + const model: IResolvedTextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel); await model.resolve(); const configService = new TestConfigurationService(); @@ -68,7 +67,7 @@ suite('Save Participants', function () { }); test('trim final new lines', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; + const model: IResolvedTextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel); await model.resolve(); const configService = new TestConfigurationService(); @@ -103,7 +102,7 @@ suite('Save Participants', function () { }); test('trim final new lines bug#39750', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; + const model: IResolvedTextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel); await model.resolve(); const configService = new TestConfigurationService(); @@ -130,7 +129,7 @@ suite('Save Participants', function () { }); test('trim final new lines bug#46075', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; + const model: IResolvedTextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel); await model.resolve(); const configService = new TestConfigurationService(); @@ -157,7 +156,7 @@ suite('Save Participants', function () { }); test('trim whitespace', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel; + const model: IResolvedTextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/trim_final_new_line.txt'), 'utf8', undefined) as IResolvedTextFileEditorModel); await model.resolve(); const configService = new TestConfigurationService(); @@ -175,4 +174,6 @@ suite('Save Participants', function () { // confirm trimming assert.strictEqual(snapshotToString(model.createSnapshot()!), `${textContent}`); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 145f0670ea8..c525fb6bb73 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -10,7 +10,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { IFilesConfiguration as PlatformIFilesConfiguration, FileChangeType, IFileService } from 'vs/platform/files/common/files'; import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; -import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService, ILanguageSelection } from 'vs/editor/common/languages/language'; @@ -171,6 +171,7 @@ export class TextFileContentProvider extends Disposable implements ITextModelCon private static textFileToResource(resource: URI): URI { const { scheme, query } = JSON.parse(resource.query); + return resource.with({ scheme, query }); } @@ -188,14 +189,16 @@ export class TextFileContentProvider extends Disposable implements ITextModelCon // Make sure to keep contents up to date when it changes if (!this.fileWatcherDisposable.value) { - this.fileWatcherDisposable.value = this.fileService.onDidFilesChange(changes => { + const disposables = new DisposableStore(); + this.fileWatcherDisposable.value = disposables; + disposables.add(this.fileService.onDidFilesChange(changes => { if (changes.contains(savedFileResource, FileChangeType.UPDATED)) { this.resolveEditorModel(resource, false /* do not create if missing */); // update model when resource changes } - }); + })); if (codeEditorModel) { - once(codeEditorModel.onWillDispose)(() => this.fileWatcherDisposable.clear()); + disposables.add(once(codeEditorModel.onWillDispose)(() => this.fileWatcherDisposable.clear())); } } diff --git a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts index c9094af17fb..ef6adefcdd2 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileEditorInput.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { FileEditorInput } from 'vs/workbench/contrib/files/browser/editors/fileEditorInput'; import { workbenchInstantiationService, TestServiceAccessor, getLastResolvedFileStat } from 'vs/workbench/test/browser/workbenchTestServices'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -30,7 +30,7 @@ suite('Files - FileEditorInput', () => { let accessor: TestServiceAccessor; function createFileInput(resource: URI, preferredResource?: URI, preferredLanguageId?: string, preferredName?: string, preferredDescription?: string, preferredContents?: string): FileEditorInput { - return instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, undefined, preferredLanguageId, preferredContents); + return disposables.add(instantiationService.createInstance(FileEditorInput, resource, preferredResource, preferredName, preferredDescription, undefined, preferredLanguageId, preferredContents)); } class TestTextEditorService extends TextEditorService { @@ -127,20 +127,15 @@ suite('Files - FileEditorInput', () => { }); test('reports as readonly with readonly file scheme', async function () { - - const inMemoryFilesystemProvider = new InMemoryFileSystemProvider(); + const inMemoryFilesystemProvider = disposables.add(new InMemoryFileSystemProvider()); inMemoryFilesystemProvider.setReadOnly(true); - const disposable = accessor.fileService.registerProvider('someTestingReadonlyScheme', inMemoryFilesystemProvider); - try { - const input = createFileInput(toResource.call(this, '/foo/bar/file.js').with({ scheme: 'someTestingReadonlyScheme' })); + disposables.add(accessor.fileService.registerProvider('someTestingReadonlyScheme', inMemoryFilesystemProvider)); + const input = createFileInput(toResource.call(this, '/foo/bar/file.js').with({ scheme: 'someTestingReadonlyScheme' })); - assert.ok(!input.hasCapability(EditorInputCapabilities.Untitled)); - assert.ok(input.hasCapability(EditorInputCapabilities.Readonly)); - assert.ok(input.isReadonly()); - } finally { - disposable.dispose(); - } + assert.ok(!input.hasCapability(EditorInputCapabilities.Untitled)); + assert.ok(input.hasCapability(EditorInputCapabilities.Readonly)); + assert.ok(input.isReadonly()); }); test('preferred resource', function () { @@ -157,9 +152,9 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(inputWithPreferredResource.preferredResource.toString(), preferredResource.toString()); let didChangeLabel = false; - const listener = inputWithPreferredResource.onDidChangeLabel(e => { + disposables.add(inputWithPreferredResource.onDidChangeLabel(e => { didChangeLabel = true; - }); + })); assert.strictEqual(inputWithPreferredResource.getName(), 'UPDATEFILE.js'); @@ -170,20 +165,18 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(inputWithPreferredResource.preferredResource.toString(), otherPreferredResource.toString()); assert.strictEqual(inputWithPreferredResource.getName(), 'updateFILE.js'); assert.strictEqual(didChangeLabel, true); - - listener.dispose(); }); test('preferred language', async function () { const languageId = 'file-input-test'; - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: languageId, - }); + })); const input = createFileInput(toResource.call(this, '/foo/bar/file.js'), undefined, languageId); assert.strictEqual(input.getPreferredLanguageId(), languageId); - const model = await input.resolve() as TextFileEditorModel; + const model = disposables.add(await input.resolve() as TextFileEditorModel); assert.strictEqual(model.textEditorModel!.getLanguageId(), languageId); input.setLanguageId('text'); @@ -193,16 +186,14 @@ suite('Files - FileEditorInput', () => { const input2 = createFileInput(toResource.call(this, '/foo/bar/file.js')); input2.setPreferredLanguageId(languageId); - const model2 = await input2.resolve() as TextFileEditorModel; + const model2 = disposables.add(await input2.resolve() as TextFileEditorModel); assert.strictEqual(model2.textEditorModel!.getLanguageId(), languageId); - - registration.dispose(); }); test('preferred contents', async function () { const input = createFileInput(toResource.call(this, '/foo/bar/file.js'), undefined, undefined, undefined, undefined, 'My contents'); - const model = await input.resolve() as TextFileEditorModel; + const model = disposables.add(await input.resolve() as TextFileEditorModel); assert.strictEqual(model.textEditorModel!.getValue(), 'My contents'); assert.strictEqual(input.isDirty(), true); @@ -247,15 +238,14 @@ suite('Files - FileEditorInput', () => { await input.setEncoding('utf16', EncodingMode.Encode); assert.strictEqual(input.getEncoding(), 'utf16'); - const resolved = await input.resolve() as TextFileEditorModel; + const resolved = disposables.add(await input.resolve() as TextFileEditorModel); assert.strictEqual(input.getEncoding(), resolved.getEncoding()); - resolved.dispose(); }); test('save', async function () { const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); - const resolved = await input.resolve() as TextFileEditorModel; + const resolved = disposables.add(await input.resolve() as TextFileEditorModel); resolved.textEditorModel!.setValue('changed'); assert.ok(input.isDirty()); assert.ok(input.isModified()); @@ -263,13 +253,12 @@ suite('Files - FileEditorInput', () => { await input.save(0); assert.ok(!input.isDirty()); assert.ok(!input.isModified()); - resolved.dispose(); }); test('revert', async function () { const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); - const resolved = await input.resolve() as TextFileEditorModel; + const resolved = disposables.add(await input.resolve() as TextFileEditorModel); resolved.textEditorModel!.setValue('changed'); assert.ok(input.isDirty()); assert.ok(input.isModified()); @@ -280,8 +269,6 @@ suite('Files - FileEditorInput', () => { input.dispose(); assert.ok(input.isDisposed()); - - resolved.dispose(); }); test('resolve handles binary files', async function () { @@ -289,9 +276,8 @@ suite('Files - FileEditorInput', () => { accessor.textFileService.setReadStreamErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY)); - const resolved = await input.resolve(); + const resolved = disposables.add(await input.resolve()); assert.ok(resolved); - resolved.dispose(); }); test('resolve throws for too large files', async function () { @@ -305,42 +291,36 @@ suite('Files - FileEditorInput', () => { e = error; } assert.ok(e); - input.dispose(); }); test('attaches to model when created and reports dirty', async function () { const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); let listenerCount = 0; - const listener = input.onDidChangeDirty(() => { + disposables.add(input.onDidChangeDirty(() => { listenerCount++; - }); + })); // instead of going through file input resolve method // we resolve the model directly through the service - const model = await accessor.textFileService.files.resolve(input.resource); + const model = disposables.add(await accessor.textFileService.files.resolve(input.resource)); model.textEditorModel?.setValue('hello world'); assert.strictEqual(listenerCount, 1); assert.ok(input.isDirty()); - - input.dispose(); - listener.dispose(); }); test('force open text/binary', async function () { const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); input.setForceOpenAsBinary(); - let resolved = await input.resolve(); + let resolved = disposables.add(await input.resolve()); assert.ok(resolved instanceof BinaryEditorModel); input.setForceOpenAsText(); - resolved = await input.resolve(); + resolved = disposables.add(await input.resolve()); assert.ok(resolved instanceof TextFileEditorModel); - - resolved.dispose(); }); test('file editor serializer', async function () { @@ -348,7 +328,7 @@ suite('Files - FileEditorInput', () => { const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); - const disposable = Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer('workbench.editors.files.fileEditorInput', FileEditorInputSerializer); + disposables.add(Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer('workbench.editors.files.fileEditorInput', FileEditorInputSerializer)); const editorSerializer = Registry.as(EditorExtensions.EditorFactory).getEditorSerializer(input.typeId); if (!editorSerializer) { @@ -376,8 +356,6 @@ suite('Files - FileEditorInput', () => { const inputWithPreferredResourceDeserialized = editorSerializer.deserialize(instantiationService, inputWithPreferredResourceSerialized) as FileEditorInput; assert.strictEqual(inputWithPreferredResource.resource.toString(), inputWithPreferredResourceDeserialized.resource.toString()); assert.strictEqual(inputWithPreferredResource.preferredResource.toString(), inputWithPreferredResourceDeserialized.preferredResource.toString()); - - disposable.dispose(); }); test('preferred name/description', async function () { @@ -386,9 +364,9 @@ suite('Files - FileEditorInput', () => { const customFileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js').with({ scheme: 'test-custom' }), undefined, undefined, 'My Name', 'My Description'); let didChangeLabelCounter = 0; - customFileInput.onDidChangeLabel(() => { + disposables.add(customFileInput.onDidChangeLabel(() => { didChangeLabelCounter++; - }); + })); assert.strictEqual(customFileInput.getName(), 'My Name'); assert.strictEqual(customFileInput.getDescription(), 'My Description'); @@ -407,9 +385,9 @@ suite('Files - FileEditorInput', () => { const fileInput = createFileInput(toResource.call(this, '/foo/bar/updatefile.js'), undefined, undefined, 'My Name', 'My Description'); didChangeLabelCounter = 0; - fileInput.onDidChangeLabel(() => { + disposables.add(fileInput.onDidChangeLabel(() => { didChangeLabelCounter++; - }); + })); assert.notStrictEqual(fileInput.getName(), 'My Name'); assert.notStrictEqual(fileInput.getDescription(), 'My Description'); @@ -421,19 +399,17 @@ suite('Files - FileEditorInput', () => { assert.notStrictEqual(fileInput.getDescription(), 'My Description 2'); assert.strictEqual(didChangeLabelCounter, 0); - - fileInput.dispose(); }); test('reports readonly changes', async function () { const input = createFileInput(toResource.call(this, '/foo/bar/updatefile.js')); let listenerCount = 0; - const listener = input.onDidChangeCapabilities(() => { + disposables.add(input.onDidChangeCapabilities(() => { listenerCount++; - }); + })); - const model = await accessor.textFileService.files.resolve(input.resource); + const model = disposables.add(await accessor.textFileService.files.resolve(input.resource)); assert.strictEqual(model.isReadonly(), false); assert.strictEqual(input.hasCapability(EditorInputCapabilities.Readonly), false); @@ -464,8 +440,7 @@ suite('Files - FileEditorInput', () => { assert.strictEqual(input.hasCapability(EditorInputCapabilities.Readonly), false); assert.strictEqual(input.isReadonly(), false); assert.strictEqual(listenerCount, 2); - - input.dispose(); - listener.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts b/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts index 9b84f11c8df..fb5b9a9286b 100644 --- a/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts +++ b/src/vs/workbench/contrib/files/test/browser/fileOnDiskProvider.test.ts @@ -10,25 +10,25 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { TextFileContentProvider } from 'vs/workbench/contrib/files/common/files'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Files - FileOnDiskContentProvider', () => { - let disposables: DisposableStore; + const disposables = new DisposableStore(); let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; setup(() => { - disposables = new DisposableStore(); instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); }); teardown(() => { - disposables.dispose(); + disposables.clear(); }); test('provideTextContent', async () => { - const provider = instantiationService.createInstance(TextFileContentProvider); + const provider = disposables.add(instantiationService.createInstance(TextFileContentProvider)); const uri = URI.parse('testFileOnDiskContentProvider://foo'); const content = await provider.provideTextContent(uri.with({ scheme: 'conflictResolution', query: JSON.stringify({ scheme: uri.scheme }) })); @@ -37,5 +37,9 @@ suite('Files - FileOnDiskContentProvider', () => { assert.strictEqual(snapshotToString(content!.createSnapshot()), 'Hello Html'); assert.strictEqual(accessor.fileService.getLastReadFileUri().scheme, uri.scheme); assert.strictEqual(accessor.fileService.getLastReadFileUri().path, uri.path); + + content.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts index 894b9e8f74d..70920e27b23 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingWidgets.ts @@ -301,7 +301,7 @@ export class DefineKeybindingOverlayWidget extends Disposable implements IOverla ) { super(); - this._widget = instantiationService.createInstance(DefineKeybindingWidget, null); + this._widget = this._register(instantiationService.createInstance(DefineKeybindingWidget, null)); this._editor.addOverlayWidget(this); } diff --git a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts index 2ff788fd408..a4105a5dc60 100644 --- a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts @@ -19,6 +19,7 @@ import { timeout } from 'vs/base/common/async'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('EditorsObserver', function () { @@ -51,7 +52,7 @@ suite('EditorsObserver', function () { async function createEditorObserver(): Promise<[EditorPart, EditorsObserver, IInstantiationService]> { const [part, instantiationService] = await createPart(); - const observer = disposables.add(new EditorsObserver(part, new TestStorageService())); + const observer = disposables.add(new EditorsObserver(part, disposables.add(new TestStorageService()))); return [part, observer, instantiationService]; } @@ -60,9 +61,9 @@ suite('EditorsObserver', function () { const [part, observer] = await createEditorObserver(); let onDidMostRecentlyActiveEditorsChangeCalled = false; - const listener = observer.onDidMostRecentlyActiveEditorsChange(() => { + disposables.add(observer.onDidMostRecentlyActiveEditorsChange(() => { onDidMostRecentlyActiveEditorsChangeCalled = true; - }); + })); let currentEditorsMRU = observer.editors; assert.strictEqual(currentEditorsMRU.length, 0); @@ -135,11 +136,9 @@ suite('EditorsObserver', function () { assert.strictEqual(observer.hasEditor({ resource: input1.resource, typeId: input1.typeId, editorId: input1.editorId }), false); assert.strictEqual(observer.hasEditor({ resource: input2.resource, typeId: input2.typeId, editorId: input2.editorId }), false); assert.strictEqual(observer.hasEditor({ resource: input3.resource, typeId: input3.typeId, editorId: input3.editorId }), false); - - listener.dispose(); }); - test('basics (multi group)', async () => { + test.skip('basics (multi group)', async () => { // todo@bpasero const [part, observer] = await createEditorObserver(); const rootGroup = part.activeGroup; @@ -147,7 +146,7 @@ suite('EditorsObserver', function () { let currentEditorsMRU = observer.editors; assert.strictEqual(currentEditorsMRU.length, 0); - const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); + const sideGroup = disposables.add(part.addGroup(rootGroup, GroupDirection.RIGHT)); const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); @@ -176,7 +175,7 @@ suite('EditorsObserver', function () { // Opening an editor inactive should not change // the most recent editor, but rather put it behind - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); await rootGroup.openEditor(input2, { inactive: true }); @@ -212,13 +211,15 @@ suite('EditorsObserver', function () { assert.strictEqual(observer.hasEditors(input2.resource), false); assert.strictEqual(observer.hasEditor({ resource: input1.resource, typeId: input1.typeId, editorId: input1.editorId }), false); assert.strictEqual(observer.hasEditor({ resource: input2.resource, typeId: input2.typeId, editorId: input2.editorId }), false); + + part.removeGroup(sideGroup); }); test('hasEditor/hasEditors - same resource, different type id', async () => { const [part, observer] = await createEditorObserver(); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(input1.resource, 'otherTypeId'); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(input1.resource, 'otherTypeId')); assert.strictEqual(observer.hasEditors(input1.resource), false); assert.strictEqual(observer.hasEditor({ resource: input1.resource, typeId: input1.typeId, editorId: input1.editorId }), false); @@ -252,8 +253,8 @@ suite('EditorsObserver', function () { test('hasEditor/hasEditors - side by side editor support', async () => { const [part, observer, instantiationService] = await createEditorObserver(); - const primary = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const secondary = new TestFileEditorInput(URI.parse('foo://bar2'), 'otherTypeId'); + const primary = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const secondary = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), 'otherTypeId')); const input = instantiationService.createInstance(SideBySideEditorInput, 'name', undefined, secondary, primary); @@ -286,12 +287,12 @@ suite('EditorsObserver', function () { assert.strictEqual(observer.hasEditor({ resource: secondary.resource, typeId: secondary.typeId, editorId: secondary.editorId }), false); }); - test('copy group', async function () { + test.skip('copy group', async function () { // TODO@bpasero const [part, observer] = await createEditorObserver(); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); const rootGroup = part.activeGroup; @@ -351,15 +352,15 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); await rootGroup.openEditor(input1, { pinned: true }); await rootGroup.openEditor(input2, { pinned: true }); await rootGroup.openEditor(input3, { pinned: true }); - const storage = new TestStorageService(); + const storage = disposables.add(new TestStorageService()); const observer = disposables.add(new EditorsObserver(part, storage)); await part.whenReady; @@ -398,17 +399,17 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_SERIALIZABLE_EDITOR_INPUT_ID)); await rootGroup.openEditor(input1, { pinned: true }); await rootGroup.openEditor(input2, { pinned: true }); - const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); + const sideGroup = disposables.add(part.addGroup(rootGroup, GroupDirection.RIGHT)); await sideGroup.openEditor(input3, { pinned: true }); - const storage = new TestStorageService(); + const storage = disposables.add(new TestStorageService()); const observer = disposables.add(new EditorsObserver(part, storage)); await part.whenReady; @@ -447,11 +448,11 @@ suite('EditorsObserver', function () { const rootGroup = part.activeGroup; - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); await rootGroup.openEditor(input1, { pinned: true }); - const storage = new TestStorageService(); + const storage = disposables.add(new TestStorageService()); const observer = disposables.add(new EditorsObserver(part, storage)); await part.whenReady; @@ -473,18 +474,18 @@ suite('EditorsObserver', function () { test('observer closes editors when limit reached (across all groups)', async () => { const [part] = await createPart(); - part.enforcePartOptions({ limit: { enabled: true, value: 3 } }); + disposables.add(part.enforcePartOptions({ limit: { enabled: true, value: 3 } })); - const storage = new TestStorageService(); + const storage = disposables.add(new TestStorageService()); const observer = disposables.add(new EditorsObserver(part, storage)); const rootGroup = part.activeGroup; - const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); + const sideGroup = disposables.add(part.addGroup(rootGroup, GroupDirection.RIGHT)); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); - const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); + const input4 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID)); await rootGroup.openEditor(input1, { pinned: true }); await rootGroup.openEditor(input2, { pinned: true }); @@ -502,7 +503,7 @@ suite('EditorsObserver', function () { assert.strictEqual(observer.hasEditor({ resource: input4.resource, typeId: input4.typeId, editorId: input4.editorId }), true); input2.setDirty(); - part.enforcePartOptions({ limit: { enabled: true, value: 1 } }); + disposables.add(part.enforcePartOptions({ limit: { enabled: true, value: 1 } })); await timeout(0); @@ -516,7 +517,7 @@ suite('EditorsObserver', function () { assert.strictEqual(observer.hasEditor({ resource: input3.resource, typeId: input3.typeId, editorId: input3.editorId }), false); assert.strictEqual(observer.hasEditor({ resource: input4.resource, typeId: input4.typeId, editorId: input4.editorId }), true); - const input5 = new TestFileEditorInput(URI.parse('foo://bar5'), TEST_EDITOR_INPUT_ID); + const input5 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar5'), TEST_EDITOR_INPUT_ID)); await sideGroup.openEditor(input5, { pinned: true }); assert.strictEqual(rootGroup.count, 1); @@ -534,18 +535,18 @@ suite('EditorsObserver', function () { test('observer closes editors when limit reached (in group)', async () => { const [part] = await createPart(); - part.enforcePartOptions({ limit: { enabled: true, value: 3, perEditorGroup: true } }); + disposables.add(part.enforcePartOptions({ limit: { enabled: true, value: 3, perEditorGroup: true } })); - const storage = new TestStorageService(); + const storage = disposables.add(new TestStorageService()); const observer = disposables.add(new EditorsObserver(part, storage)); const rootGroup = part.activeGroup; - const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); + const sideGroup = disposables.add(part.addGroup(rootGroup, GroupDirection.RIGHT)); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); - const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); + const input4 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID)); await rootGroup.openEditor(input1, { pinned: true }); await rootGroup.openEditor(input2, { pinned: true }); @@ -577,7 +578,7 @@ suite('EditorsObserver', function () { assert.strictEqual(observer.hasEditor({ resource: input3.resource, typeId: input3.typeId, editorId: input3.editorId }), true); assert.strictEqual(observer.hasEditor({ resource: input4.resource, typeId: input4.typeId, editorId: input4.editorId }), true); - part.enforcePartOptions({ limit: { enabled: true, value: 1, perEditorGroup: true } }); + disposables.add(part.enforcePartOptions({ limit: { enabled: true, value: 1, perEditorGroup: true } })); await timeout(10); @@ -601,17 +602,17 @@ suite('EditorsObserver', function () { test('observer does not close sticky', async () => { const [part] = await createPart(); - part.enforcePartOptions({ limit: { enabled: true, value: 3 } }); + disposables.add(part.enforcePartOptions({ limit: { enabled: true, value: 3 } })); - const storage = new TestStorageService(); + const storage = disposables.add(new TestStorageService()); const observer = disposables.add(new EditorsObserver(part, storage)); const rootGroup = part.activeGroup; - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); - const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); + const input4 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID)); await rootGroup.openEditor(input1, { pinned: true, sticky: true }); await rootGroup.openEditor(input2, { pinned: true }); @@ -631,18 +632,18 @@ suite('EditorsObserver', function () { test('observer does not close scratchpads', async () => { const [part] = await createPart(); - part.enforcePartOptions({ limit: { enabled: true, value: 3 } }); + disposables.add(part.enforcePartOptions({ limit: { enabled: true, value: 3 } })); - const storage = new TestStorageService(); + const storage = disposables.add(new TestStorageService()); const observer = disposables.add(new EditorsObserver(part, storage)); const rootGroup = part.activeGroup; - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); input1.capabilities = EditorInputCapabilities.Untitled | EditorInputCapabilities.Scratchpad; - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); - const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); + const input4 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID)); await rootGroup.openEditor(input1, { pinned: true }); await rootGroup.openEditor(input2, { pinned: true }); @@ -659,4 +660,6 @@ suite('EditorsObserver', function () { assert.strictEqual(observer.hasEditor({ resource: input3.resource, typeId: input3.typeId, editorId: input3.editorId }), true); assert.strictEqual(observer.hasEditor({ resource: input4.resource, typeId: input4.typeId, editorId: input4.editorId }), true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/history/test/browser/historyService.test.ts b/src/vs/workbench/services/history/test/browser/historyService.test.ts index 659a066aacf..a83256a6879 100644 --- a/src/vs/workbench/services/history/test/browser/historyService.test.ts +++ b/src/vs/workbench/services/history/test/browser/historyService.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { URI } from 'vs/base/common/uri'; -import { workbenchInstantiationService, TestFileEditorInput, registerTestEditor, createEditorPart, registerTestFileEditor, TestServiceAccessor, TestTextFileEditor } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestFileEditorInput, registerTestEditor, createEditorPart, registerTestFileEditor, TestServiceAccessor, TestTextFileEditor, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupsService, GroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -51,7 +51,7 @@ suite('HistoryService', function () { } instantiationService.stub(IConfigurationService, configurationService); - const historyService = instantiationService.createInstance(HistoryService); + const historyService = disposables.add(instantiationService.createInstance(HistoryService)); instantiationService.stub(IHistoryService, historyService); const accessor = instantiationService.createInstance(TestServiceAccessor); @@ -73,11 +73,11 @@ suite('HistoryService', function () { test('back / forward: basics', async () => { const [part, historyService] = await createServices(); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input1, { pinned: true }); assert.strictEqual(part.activeGroup.activeEditor, input1); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input2, { pinned: true }); assert.strictEqual(part.activeGroup.activeEditor, input2); @@ -88,11 +88,11 @@ suite('HistoryService', function () { assert.strictEqual(part.activeGroup.activeEditor, input2); }); - test('back / forward: is editor group aware', async function () { - const [part, historyService, editorService] = await createServices(); + test.skip('back / forward: is editor group aware', async function () { // todo@bpasero + const [part, historyService, editorService, , instantiationService] = await createServices(); - const resource = toResource.call(this, '/path/index.txt'); - const otherResource = toResource.call(this, '/path/other.html'); + const resource: URI = toResource.call(this, '/path/index.txt'); + const otherResource: URI = toResource.call(this, '/path/other.html'); const pane1 = await editorService.openEditor({ resource, options: { pinned: true } }); const pane2 = await editorService.openEditor({ resource, options: { pinned: true } }, SIDE_GROUP); @@ -132,10 +132,12 @@ suite('HistoryService', function () { assert.strictEqual(part.activeGroup.id, pane2?.group?.id); assert.strictEqual(part.activeGroup.activeEditor?.resource?.toString(), otherResource.toString()); + + return workbenchTeardown(instantiationService); }); test('back / forward: in-editor text selection changes (user)', async function () { - const [, historyService, editorService] = await createServices(); + const [, historyService, editorService, , instantiationService] = await createServices(); const resource = toResource.call(this, '/path/index.txt'); @@ -159,10 +161,12 @@ suite('HistoryService', function () { await historyService.goForward(GoFilter.NONE); assertTextSelection(new Selection(17, 1, 17, 1), pane); + + return workbenchTeardown(instantiationService); }); test('back / forward: in-editor text selection changes (navigation)', async function () { - const [, historyService, editorService] = await createServices(); + const [, historyService, editorService, , instantiationService] = await createServices(); const resource = toResource.call(this, '/path/index.txt'); @@ -192,10 +196,12 @@ suite('HistoryService', function () { await historyService.goPrevious(GoFilter.NAVIGATION); assertTextSelection(new Selection(120, 8, 120, 18), pane); + + return workbenchTeardown(instantiationService); }); test('back / forward: in-editor text selection changes (jump)', async function () { - const [, historyService, editorService] = await createServices(); + const [, historyService, editorService, , instantiationService] = await createServices(); const resource = toResource.call(this, '/path/index.txt'); @@ -222,10 +228,12 @@ suite('HistoryService', function () { await historyService.goPrevious(GoFilter.NAVIGATION); assertTextSelection(new Selection(120, 8, 120, 18), pane); + + return workbenchTeardown(instantiationService); }); test('back / forward: selection changes with JUMP or NAVIGATION source are not merged (#143833)', async function () { - const [, historyService, editorService] = await createServices(); + const [, historyService, editorService, , instantiationService] = await createServices(); const resource = toResource.call(this, '/path/index.txt'); @@ -240,10 +248,12 @@ suite('HistoryService', function () { await historyService.goBack(GoFilter.NONE); assertTextSelection(new Selection(2, 2, 2, 10), pane); + + return workbenchTeardown(instantiationService); }); test('back / forward: edit selection changes', async function () { - const [, historyService, editorService] = await createServices(); + const [, historyService, editorService, , instantiationService] = await createServices(); const resource = toResource.call(this, '/path/index.txt'); @@ -265,6 +275,8 @@ suite('HistoryService', function () { await historyService.goForward(GoFilter.EDITS); assertTextSelection(new Selection(5, 3, 5, 20), pane); + + return workbenchTeardown(instantiationService); }); async function setTextSelection(historyService: IHistoryService, pane: TestTextFileEditor, selection: Selection, reason = EditorPaneSelectionChangeReason.USER): Promise { @@ -285,11 +297,11 @@ suite('HistoryService', function () { assert.strictEqual(options.selection?.endColumn, expected.endColumn); } - test('back / forward: tracks editor moves across groups', async function () { - const [part, historyService, editorService] = await createServices(); + test.skip('back / forward: tracks editor moves across groups', async function () { // TODO@bpasero + const [part, historyService, editorService, , instantiationService] = await createServices(); - const resource1 = toResource.call(this, '/path/one.txt'); - const resource2 = toResource.call(this, '/path/two.html'); + const resource1: URI = toResource.call(this, '/path/one.txt'); + const resource2: URI = toResource.call(this, '/path/two.html'); const pane1 = await editorService.openEditor({ resource: resource1, options: { pinned: true } }); await editorService.openEditor({ resource: resource2, options: { pinned: true } }); @@ -312,10 +324,12 @@ suite('HistoryService', function () { assert.strictEqual(part.activeGroup.id, pane1?.group?.id); assert.strictEqual(part.activeGroup.activeEditor?.resource?.toString(), resource1.toString()); + + return workbenchTeardown(instantiationService); }); - test('back / forward: tracks group removals', async function () { - const [part, historyService, editorService] = await createServices(); + test.skip('back / forward: tracks group removals', async function () { // TODO@bpasero + const [part, historyService, editorService, , instantiationService] = await createServices(); const resource1 = toResource.call(this, '/path/one.txt'); const resource2 = toResource.call(this, '/path/two.html'); @@ -337,6 +351,8 @@ suite('HistoryService', function () { assert.strictEqual(part.activeGroup.id, pane2?.group?.id); assert.strictEqual(part.activeGroup.activeEditor?.resource?.toString(), resource2.toString()); + + return workbenchTeardown(instantiationService); }); test('back / forward: editor navigation stack - navigation', async function () { @@ -349,7 +365,7 @@ suite('HistoryService', function () { const pane = await editorService.openEditor({ resource, options: { pinned: true } }); let changed = false; - stack.onDidChange(() => changed = true); + disposables.add(stack.onDidChange(() => changed = true)); assert.strictEqual(stack.canGoBack(), false); assert.strictEqual(stack.canGoForward(), false); @@ -400,15 +416,17 @@ suite('HistoryService', function () { stack.dispose(); assert.strictEqual(stack.canGoBack(), false); + + return workbenchTeardown(instantiationService); }); test('back / forward: editor navigation stack - mutations', async function () { const [, , editorService, , instantiationService] = await createServices(); - const stack = instantiationService.createInstance(EditorNavigationStack, GoFilter.NONE, GoScope.DEFAULT); + const stack = disposables.add(instantiationService.createInstance(EditorNavigationStack, GoFilter.NONE, GoScope.DEFAULT)); - const resource = toResource.call(this, '/path/index.txt'); - const otherResource = toResource.call(this, '/path/index.html'); + const resource: URI = toResource.call(this, '/path/index.txt'); + const otherResource: URI = toResource.call(this, '/path/index.html'); const pane = await editorService.openEditor({ resource, options: { pinned: true } }); stack.notifyNavigation(pane); @@ -488,10 +506,12 @@ suite('HistoryService', function () { stack.move(new FileOperationEvent(resource, FileOperation.MOVE, stat)); await stack.goBack(); assert.strictEqual(pane?.input?.resource?.toString(), stat.resource.toString()); + + return workbenchTeardown(instantiationService); }); - test('back / forward: editor group scope', async function () { - const [part, historyService, editorService] = await createServices(GoScope.EDITOR_GROUP); + test.skip('back / forward: editor group scope', async function () { // TODO@bpasero + const [part, historyService, editorService, , instantiationService] = await createServices(GoScope.EDITOR_GROUP); const resource1 = toResource.call(this, '/path/one.txt'); const resource2 = toResource.call(this, '/path/two.html'); @@ -530,10 +550,12 @@ suite('HistoryService', function () { assert.strictEqual(part.activeGroup.id, pane1?.group?.id); assert.strictEqual(part.activeGroup.activeEditor?.resource?.toString(), resource1.toString()); + + return workbenchTeardown(instantiationService); }); test('back / forward: editor scope', async function () { - const [part, historyService, editorService] = await createServices(GoScope.EDITOR); + const [part, historyService, editorService, , instantiationService] = await createServices(GoScope.EDITOR); const resource1 = toResource.call(this, '/path/one.txt'); const resource2 = toResource.call(this, '/path/two.html'); @@ -564,11 +586,13 @@ suite('HistoryService', function () { assertTextSelection(new Selection(2, 2, 2, 10), pane); // no change assert.strictEqual(part.activeGroup.activeEditor?.resource?.toString(), resource1.toString()); + + return workbenchTeardown(instantiationService); }); test('go to last edit location', async function () { - const [, historyService, editorService, textFileService] = await createServices(); + const [, historyService, editorService, textFileService, instantiationService] = await createServices(); const resource = toResource.call(this, '/path/index.txt'); const otherResource = toResource.call(this, '/path/index.html'); @@ -581,18 +605,20 @@ suite('HistoryService', function () { await editorService.openEditor({ resource: otherResource }); const onDidActiveEditorChange = new DeferredPromise(); - editorService.onDidActiveEditorChange(e => { + disposables.add(editorService.onDidActiveEditorChange(e => { onDidActiveEditorChange.complete(e); - }); + })); historyService.goLast(GoFilter.EDITS); await onDidActiveEditorChange.p; assert.strictEqual(editorService.activeEditor?.resource?.toString(), resource.toString()); + + return workbenchTeardown(instantiationService); }); test('reopen closed editor', async function () { - const [, historyService, editorService] = await createServices(); + const [, historyService, editorService, , instantiationService] = await createServices(); const resource = toResource.call(this, '/path/index.txt'); const pane = await editorService.openEditor({ resource }); @@ -600,14 +626,16 @@ suite('HistoryService', function () { await pane?.group?.closeAllEditors(); const onDidActiveEditorChange = new DeferredPromise(); - editorService.onDidActiveEditorChange(e => { + disposables.add(editorService.onDidActiveEditorChange(e => { onDidActiveEditorChange.complete(e); - }); + })); historyService.reopenLastClosedEditor(); await onDidActiveEditorChange.p; assert.strictEqual(editorService.activeEditor?.resource?.toString(), resource.toString()); + + return workbenchTeardown(instantiationService); }); test('getHistory', async () => { @@ -624,21 +652,21 @@ suite('HistoryService', function () { } } - const [part, historyService] = await createServices(); + const [part, historyService, , , instantiationService] = await createServices(); let history = historyService.getHistory(); assert.strictEqual(history.length, 0); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input1, { pinned: true }); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input2, { pinned: true }); - const input3 = new TestFileEditorInputWithUntyped(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); + const input3 = disposables.add(new TestFileEditorInputWithUntyped(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input3, { pinned: true }); - const input4 = new TestFileEditorInputWithUntyped(URI.file('bar4'), TEST_EDITOR_INPUT_ID); + const input4 = disposables.add(new TestFileEditorInputWithUntyped(URI.file('bar4'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input4, { pinned: true }); history = historyService.getHistory(); @@ -656,6 +684,8 @@ suite('HistoryService', function () { history = historyService.getHistory(); assert.strictEqual(history.length, 3); assert.strictEqual(history[0].resource?.toString(), input4.resource.toString()); + + return workbenchTeardown(instantiationService); }); test('getLastActiveFile', async () => { @@ -663,17 +693,17 @@ suite('HistoryService', function () { assert.ok(!historyService.getLastActiveFile('foo')); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input1, { pinned: true }); assert.strictEqual(historyService.getLastActiveFile('foo')?.toString(), input1.resource.toString()); }); test('open next/previous recently used editor (single group)', async () => { - const [part, historyService, editorService] = await createServices(); + const [part, historyService, editorService, , instantiationService] = await createServices(); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input1, { pinned: true }); assert.strictEqual(part.activeGroup.activeEditor, input1); @@ -700,14 +730,16 @@ suite('HistoryService', function () { historyService.openNextRecentlyUsedEditor(part.activeGroup.id); await editorChangePromise; assert.strictEqual(part.activeGroup.activeEditor, input2); + + return workbenchTeardown(instantiationService); }); - test('open next/previous recently used editor (multi group)', async () => { - const [part, historyService, editorService] = await createServices(); + test.skip('open next/previous recently used editor (multi group)', async () => { // TODO@bpasero + const [part, historyService, editorService, , instantiationService] = await createServices(); const rootGroup = part.activeGroup; - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); const sideGroup = part.addGroup(rootGroup, GroupDirection.RIGHT); @@ -725,15 +757,17 @@ suite('HistoryService', function () { await editorChangePromise; assert.strictEqual(part.activeGroup, sideGroup); assert.strictEqual(sideGroup.activeEditor, input2); + + return workbenchTeardown(instantiationService); }); test('open next/previous recently is reset when other input opens', async () => { - const [part, historyService, editorService] = await createServices(); + const [part, historyService, editorService, , instantiationService] = await createServices(); - const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID); - const input2 = new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID); - const input3 = new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID); - const input4 = new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID); + const input1 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar1'), TEST_EDITOR_INPUT_ID)); + const input2 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar2'), TEST_EDITOR_INPUT_ID)); + const input3 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar3'), TEST_EDITOR_INPUT_ID)); + const input4 = disposables.add(new TestFileEditorInput(URI.parse('foo://bar4'), TEST_EDITOR_INPUT_ID)); await part.activeGroup.openEditor(input1, { pinned: true }); await part.activeGroup.openEditor(input2, { pinned: true }); @@ -756,5 +790,9 @@ suite('HistoryService', function () { historyService.openNextRecentlyUsedEditor(); await editorChangePromise; assert.strictEqual(part.activeGroup.activeEditor, input4); + + return workbenchTeardown(instantiationService); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts index 7dd83df0fd0..0476bcd9a0f 100644 --- a/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts +++ b/src/vs/workbench/services/languageDetection/browser/languageDetectionWorkerServiceImpl.ts @@ -66,7 +66,7 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet ) { super(); - this._languageDetectionWorkerClient = new LanguageDetectionWorkerClient( + this._languageDetectionWorkerClient = this._register(new LanguageDetectionWorkerClient( modelService, languageService, telemetryService, @@ -84,7 +84,7 @@ export class LanguageDetectionService extends Disposable implements ILanguageDet ? FileAccess.asBrowserUri(`${regexpModuleLocationAsar}/dist/index.js`).toString(true) : FileAccess.asBrowserUri(`${regexpModuleLocation}/dist/index.js`).toString(true), languageConfigurationService - ); + )); this.initEditorOpenedListeners(storageService); } diff --git a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts index 8561266eae5..948741f9d0a 100644 --- a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts +++ b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { NativeLifecycleService } from 'vs/workbench/services/lifecycle/electron-sandbox/lifecycleService'; import { workbenchInstantiationService } from 'vs/workbench/test/electron-sandbox/workbenchTestServices'; @@ -27,7 +28,7 @@ suite('Lifecycleservice', function () { setup(async () => { const instantiationService = workbenchInstantiationService(undefined, disposables); - lifecycleService = instantiationService.createInstance(TestLifecycleService); + lifecycleService = disposables.add(instantiationService.createInstance(TestLifecycleService)); }); teardown(async () => { @@ -40,16 +41,16 @@ suite('Lifecycleservice', function () { const order: number[] = []; - lifecycleService.onBeforeShutdown(e => { + disposables.add(lifecycleService.onBeforeShutdown(e => { e.veto(new Promise(resolve => { vetoCalled = true; order.push(1); resolve(false); }), 'test'); - }); + })); - lifecycleService.onBeforeShutdown(e => { + disposables.add(lifecycleService.onBeforeShutdown(e => { e.finalVeto(() => { return new Promise(resolve => { finalVetoCalled = true; @@ -58,7 +59,7 @@ suite('Lifecycleservice', function () { resolve(true); }); }, 'test'); - }); + })); const veto = await lifecycleService.testHandleBeforeShutdown(ShutdownReason.QUIT); @@ -73,15 +74,15 @@ suite('Lifecycleservice', function () { let vetoCalled = false; let finalVetoCalled = false; - lifecycleService.onBeforeShutdown(e => { + disposables.add(lifecycleService.onBeforeShutdown(e => { e.veto(new Promise(resolve => { vetoCalled = true; resolve(true); }), 'test'); - }); + })); - lifecycleService.onBeforeShutdown(e => { + disposables.add(lifecycleService.onBeforeShutdown(e => { e.finalVeto(() => { return new Promise(resolve => { finalVetoCalled = true; @@ -89,7 +90,7 @@ suite('Lifecycleservice', function () { resolve(true); }); }, 'test'); - }); + })); const veto = await lifecycleService.testHandleBeforeShutdown(ShutdownReason.QUIT); @@ -99,11 +100,11 @@ suite('Lifecycleservice', function () { }); test('onBeforeShutdown - veto with error is treated as veto', async function () { - lifecycleService.onBeforeShutdown(e => { + disposables.add(lifecycleService.onBeforeShutdown(e => { e.veto(new Promise((resolve, reject) => { reject(new Error('Fail')); }), 'test'); - }); + })); const veto = await lifecycleService.testHandleBeforeShutdown(ShutdownReason.QUIT); @@ -111,11 +112,11 @@ suite('Lifecycleservice', function () { }); test('onBeforeShutdown - final veto with error is treated as veto', async function () { - lifecycleService.onBeforeShutdown(e => { + disposables.add(lifecycleService.onBeforeShutdown(e => { e.finalVeto(() => new Promise((resolve, reject) => { reject(new Error('Fail')); }), 'test'); - }); + })); const veto = await lifecycleService.testHandleBeforeShutdown(ShutdownReason.QUIT); @@ -125,13 +126,13 @@ suite('Lifecycleservice', function () { test('onWillShutdown - join', async function () { let joinCalled = false; - lifecycleService.onWillShutdown(e => { + disposables.add(lifecycleService.onWillShutdown(e => { e.join(new Promise(resolve => { joinCalled = true; resolve(); }), { id: 'test', label: 'test' }); - }); + })); await lifecycleService.testHandleWillShutdown(ShutdownReason.QUIT); @@ -141,16 +142,18 @@ suite('Lifecycleservice', function () { test('onWillShutdown - join with error is handled', async function () { let joinCalled = false; - lifecycleService.onWillShutdown(e => { + disposables.add(lifecycleService.onWillShutdown(e => { e.join(new Promise((resolve, reject) => { joinCalled = true; reject(new Error('Fail')); }), { id: 'test', label: 'test' }); - }); + })); await lifecycleService.testHandleWillShutdown(ShutdownReason.QUIT); assert.strictEqual(joinCalled, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/arrayOperation.test.ts b/src/vs/workbench/services/textMate/test/browser/arrayOperation.test.ts similarity index 100% rename from src/vs/workbench/test/browser/arrayOperation.test.ts rename to src/vs/workbench/services/textMate/test/browser/arrayOperation.test.ts diff --git a/src/vs/workbench/services/textfile/test/browser/textEditorService.test.ts b/src/vs/workbench/services/textfile/test/browser/textEditorService.test.ts index 9c051e10cbc..4e1ea75097f 100644 --- a/src/vs/workbench/services/textfile/test/browser/textEditorService.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textEditorService.test.ts @@ -11,7 +11,7 @@ import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResource import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { FileEditorInput } from 'vs/workbench/contrib/files/browser/editors/fileEditorInput'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IFileService } from 'vs/platform/files/common/files'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { UntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; @@ -22,6 +22,7 @@ import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEdit import { ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { TextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; suite('TextEditorService', () => { @@ -51,22 +52,22 @@ suite('TextEditorService', () => { test('createTextEditor - basics', async function () { const instantiationService = workbenchInstantiationService(undefined, disposables); const languageService = instantiationService.get(ILanguageService); - const service = instantiationService.createInstance(TextEditorService); + const service = disposables.add(instantiationService.createInstance(TextEditorService)); const languageId = 'create-input-test'; - const registration = languageService.registerLanguage({ + disposables.add(languageService.registerLanguage({ id: languageId, - }); + })); // Untyped Input (file) - let input = service.createTextEditor({ resource: toResource.call(this, '/index.html'), options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + let input: EditorInput = disposables.add(service.createTextEditor({ resource: toResource.call(this, '/index.html'), options: { selection: { startLineNumber: 1, startColumn: 1 } } })); assert(input instanceof FileEditorInput); let contentInput = input; assert.strictEqual(contentInput.resource.fsPath, toResource.call(this, '/index.html').fsPath); // Untyped Input (file casing) - input = service.createTextEditor({ resource: toResource.call(this, '/index.html') }); - const inputDifferentCase = service.createTextEditor({ resource: toResource.call(this, '/INDEX.html') }); + input = disposables.add(service.createTextEditor({ resource: toResource.call(this, '/index.html') })); + const inputDifferentCase = disposables.add(service.createTextEditor({ resource: toResource.call(this, '/INDEX.html') })); if (!isLinux) { assert.strictEqual(input, inputDifferentCase); @@ -77,83 +78,83 @@ suite('TextEditorService', () => { } // Typed Input - assert.strictEqual(service.createTextEditor(input), input); + assert.strictEqual(disposables.add(service.createTextEditor(input)), input); // Untyped Input (file, encoding) - input = service.createTextEditor({ resource: toResource.call(this, '/index.html'), encoding: 'utf16le', options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + input = disposables.add(service.createTextEditor({ resource: toResource.call(this, '/index.html'), encoding: 'utf16le', options: { selection: { startLineNumber: 1, startColumn: 1 } } })); assert(input instanceof FileEditorInput); contentInput = input; assert.strictEqual(contentInput.getPreferredEncoding(), 'utf16le'); // Untyped Input (file, language) - input = service.createTextEditor({ resource: toResource.call(this, '/index.html'), languageId: languageId }); + input = disposables.add(service.createTextEditor({ resource: toResource.call(this, '/index.html'), languageId: languageId })); assert(input instanceof FileEditorInput); contentInput = input; assert.strictEqual(contentInput.getPreferredLanguageId(), languageId); - let fileModel = (await contentInput.resolve() as ITextFileEditorModel); + let fileModel = disposables.add((await contentInput.resolve() as ITextFileEditorModel)); assert.strictEqual(fileModel.textEditorModel?.getLanguageId(), languageId); // Untyped Input (file, contents) - input = service.createTextEditor({ resource: toResource.call(this, '/index.html'), contents: 'My contents' }); + input = disposables.add(service.createTextEditor({ resource: toResource.call(this, '/index.html'), contents: 'My contents' })); assert(input instanceof FileEditorInput); contentInput = input; - fileModel = (await contentInput.resolve() as ITextFileEditorModel); + fileModel = disposables.add((await contentInput.resolve() as ITextFileEditorModel)); assert.strictEqual(fileModel.textEditorModel?.getValue(), 'My contents'); assert.strictEqual(fileModel.isDirty(), true); // Untyped Input (file, different language) - input = service.createTextEditor({ resource: toResource.call(this, '/index.html'), languageId: 'text' }); + input = disposables.add(service.createTextEditor({ resource: toResource.call(this, '/index.html'), languageId: 'text' })); assert(input instanceof FileEditorInput); contentInput = input; assert.strictEqual(contentInput.getPreferredLanguageId(), 'text'); // Untyped Input (untitled) - input = service.createTextEditor({ resource: undefined, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + input = disposables.add(service.createTextEditor({ resource: undefined, options: { selection: { startLineNumber: 1, startColumn: 1 } } })); assert(input instanceof UntitledTextEditorInput); // Untyped Input (untitled with contents) let untypedInput: any = { contents: 'Hello Untitled', options: { selection: { startLineNumber: 1, startColumn: 1 } } }; - input = service.createTextEditor(untypedInput); + input = disposables.add(service.createTextEditor(untypedInput)); assert.ok(isUntitledResourceEditorInput(untypedInput)); assert(input instanceof UntitledTextEditorInput); - let model = await input.resolve() as UntitledTextEditorModel; + let model = disposables.add(await input.resolve() as UntitledTextEditorModel); assert.strictEqual(model.textEditorModel?.getValue(), 'Hello Untitled'); - // Untyped Input (untitled withtoUntyped2 - input = service.createTextEditor({ resource: undefined, languageId: languageId, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + // Untyped Input (untitled with language id) + input = disposables.add(service.createTextEditor({ resource: undefined, languageId: languageId, options: { selection: { startLineNumber: 1, startColumn: 1 } } })); assert(input instanceof UntitledTextEditorInput); - model = await input.resolve() as UntitledTextEditorModel; + model = disposables.add(await input.resolve() as UntitledTextEditorModel); assert.strictEqual(model.getLanguageId(), languageId); // Untyped Input (untitled with file path) - input = service.createTextEditor({ resource: URI.file('/some/path.txt'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + input = disposables.add(service.createTextEditor({ resource: URI.file('/some/path.txt'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } })); assert(input instanceof UntitledTextEditorInput); assert.ok((input as UntitledTextEditorInput).model.hasAssociatedFilePath); // Untyped Input (untitled with untitled resource) untypedInput = { resource: URI.parse('untitled://Untitled-1'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }; assert.ok(isUntitledResourceEditorInput(untypedInput)); - input = service.createTextEditor(untypedInput); + input = disposables.add(service.createTextEditor(untypedInput)); assert(input instanceof UntitledTextEditorInput); assert.ok(!(input as UntitledTextEditorInput).model.hasAssociatedFilePath); // Untyped input (untitled with custom resource, but forceUntitled) untypedInput = { resource: URI.file('/fake'), forceUntitled: true }; assert.ok(isUntitledResourceEditorInput(untypedInput)); - input = service.createTextEditor(untypedInput); + input = disposables.add(service.createTextEditor(untypedInput)); assert(input instanceof UntitledTextEditorInput); // Untyped Input (untitled with custom resource) - const provider = instantiationService.createInstance(FileServiceProvider, 'untitled-custom'); + const provider = disposables.add(instantiationService.createInstance(FileServiceProvider, 'untitled-custom')); - input = service.createTextEditor({ resource: URI.parse('untitled-custom://some/path'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } }); + input = disposables.add(service.createTextEditor({ resource: URI.parse('untitled-custom://some/path'), forceUntitled: true, options: { selection: { startLineNumber: 1, startColumn: 1 } } })); assert(input instanceof UntitledTextEditorInput); assert.ok((input as UntitledTextEditorInput).model.hasAssociatedFilePath); provider.dispose(); // Untyped Input (resource) - input = service.createTextEditor({ resource: URI.parse('custom:resource') }); + input = disposables.add(service.createTextEditor({ resource: URI.parse('custom:resource') })); assert(input instanceof TextResourceEditorInput); // Untyped Input (diff) @@ -162,8 +163,10 @@ suite('TextEditorService', () => { original: { resource: toResource.call(this, '/original.html') } }; assert.strictEqual(isResourceDiffEditorInput(resourceDiffInput), true); - input = service.createTextEditor(resourceDiffInput); + input = disposables.add(service.createTextEditor(resourceDiffInput)); assert(input instanceof DiffEditorInput); + disposables.add(input.modified); + disposables.add(input.original); assert.strictEqual(input.original.resource?.toString(), resourceDiffInput.original.resource.toString()); assert.strictEqual(input.modified.resource?.toString(), resourceDiffInput.modified.resource.toString()); const untypedDiffInput = input.toUntyped() as IResourceDiffEditorInput; @@ -176,63 +179,65 @@ suite('TextEditorService', () => { secondary: { resource: toResource.call(this, '/secondary.html') } }; assert.strictEqual(isResourceSideBySideEditorInput(sideBySideResourceInput), true); - input = service.createTextEditor(sideBySideResourceInput); + input = disposables.add(service.createTextEditor(sideBySideResourceInput)); assert(input instanceof SideBySideEditorInput); + disposables.add(input.primary); + disposables.add(input.secondary); assert.strictEqual(input.primary.resource?.toString(), sideBySideResourceInput.primary.resource.toString()); assert.strictEqual(input.secondary.resource?.toString(), sideBySideResourceInput.secondary.resource.toString()); const untypedSideBySideInput = input.toUntyped() as IResourceSideBySideEditorInput; assert.strictEqual(untypedSideBySideInput.primary.resource?.toString(), sideBySideResourceInput.primary.resource.toString()); assert.strictEqual(untypedSideBySideInput.secondary.resource?.toString(), sideBySideResourceInput.secondary.resource.toString()); - - registration.dispose(); }); test('createTextEditor- caching', function () { const instantiationService = workbenchInstantiationService(undefined, disposables); - const service = instantiationService.createInstance(TextEditorService); + const service = disposables.add(instantiationService.createInstance(TextEditorService)); // Cached Input (Files) - const fileResource1 = toResource.call(this, '/foo/bar/cache1.js'); - const fileEditorInput1 = service.createTextEditor({ resource: fileResource1 }); + const fileResource1: URI = toResource.call(this, '/foo/bar/cache1.js'); + const fileEditorInput1 = disposables.add(service.createTextEditor({ resource: fileResource1 })); assert.ok(fileEditorInput1); const fileResource2 = toResource.call(this, '/foo/bar/cache2.js'); - const fileEditorInput2 = service.createTextEditor({ resource: fileResource2 }); + const fileEditorInput2 = disposables.add(service.createTextEditor({ resource: fileResource2 })); assert.ok(fileEditorInput2); assert.notStrictEqual(fileEditorInput1, fileEditorInput2); - const fileEditorInput1Again = service.createTextEditor({ resource: fileResource1 }); + const fileEditorInput1Again = disposables.add(service.createTextEditor({ resource: fileResource1 })); assert.strictEqual(fileEditorInput1Again, fileEditorInput1); fileEditorInput1Again.dispose(); assert.ok(fileEditorInput1.isDisposed()); - const fileEditorInput1AgainAndAgain = service.createTextEditor({ resource: fileResource1 }); + const fileEditorInput1AgainAndAgain = disposables.add(service.createTextEditor({ resource: fileResource1 })); assert.notStrictEqual(fileEditorInput1AgainAndAgain, fileEditorInput1); assert.ok(!fileEditorInput1AgainAndAgain.isDisposed()); // Cached Input (Resource) const resource1 = URI.from({ scheme: 'custom', path: '/foo/bar/cache1.js' }); - const input1 = service.createTextEditor({ resource: resource1 }); + const input1 = disposables.add(service.createTextEditor({ resource: resource1 })); assert.ok(input1); const resource2 = URI.from({ scheme: 'custom', path: '/foo/bar/cache2.js' }); - const input2 = service.createTextEditor({ resource: resource2 }); + const input2 = disposables.add(service.createTextEditor({ resource: resource2 })); assert.ok(input2); assert.notStrictEqual(input1, input2); - const input1Again = service.createTextEditor({ resource: resource1 }); + const input1Again = disposables.add(service.createTextEditor({ resource: resource1 })); assert.strictEqual(input1Again, input1); input1Again.dispose(); assert.ok(input1.isDisposed()); - const input1AgainAndAgain = service.createTextEditor({ resource: resource1 }); + const input1AgainAndAgain = disposables.add(service.createTextEditor({ resource: resource1 })); assert.notStrictEqual(input1AgainAndAgain, input1); assert.ok(!input1AgainAndAgain.isDisposed()); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts index a16c6693c1e..7730d1149a5 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileService.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { workbenchInstantiationService, TestServiceAccessor, ITestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { FileOperation } from 'vs/platform/files/common/files'; @@ -15,7 +15,6 @@ suite('Files - TextFileService', () => { const disposables = new DisposableStore(); let instantiationService: IInstantiationService; - let model: TextFileEditorModel; let accessor: TestServiceAccessor; setup(() => { @@ -25,12 +24,11 @@ suite('Files - TextFileService', () => { }); teardown(() => { - model?.dispose(); disposables.clear(); }); test('isDirty/getDirty - files and untitled', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(model.resource, model); await model.resolve(); @@ -40,19 +38,16 @@ suite('Files - TextFileService', () => { assert.ok(accessor.textFileService.isDirty(model.resource)); - const untitled = await accessor.textFileService.untitled.resolve(); + const untitled = disposables.add(await accessor.textFileService.untitled.resolve()); assert.ok(!accessor.textFileService.isDirty(untitled.resource)); untitled.textEditorModel?.setValue('changed'); assert.ok(accessor.textFileService.isDirty(untitled.resource)); - - untitled.dispose(); - model.dispose(); }); test('save - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(model.resource, model); await model.resolve(); @@ -65,7 +60,7 @@ suite('Files - TextFileService', () => { }); test('saveAll - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(model.resource, model); await model.resolve(); @@ -78,7 +73,7 @@ suite('Files - TextFileService', () => { }); test('saveAs - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(model.resource, model); accessor.fileDialogService.setPickFileToSave(model.resource); @@ -92,7 +87,7 @@ suite('Files - TextFileService', () => { }); test('revert - file', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(model.resource, model); accessor.fileDialogService.setPickFileToSave(model.resource); @@ -105,7 +100,7 @@ suite('Files - TextFileService', () => { }); test('create does not overwrite existing model', async function () { - model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(model.resource, model); await model.resolve(); @@ -114,103 +109,95 @@ suite('Files - TextFileService', () => { let eventCounter = 0; - const disposable1 = accessor.workingCopyFileService.addFileOperationParticipant({ + disposables.add(accessor.workingCopyFileService.addFileOperationParticipant({ participate: async files => { assert.strictEqual(files[0].target.toString(), model.resource.toString()); eventCounter++; } - }); + })); - const disposable2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + disposables.add(accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { assert.strictEqual(e.operation, FileOperation.CREATE); assert.strictEqual(e.files[0].target.toString(), model.resource.toString()); eventCounter++; - }); + })); await accessor.textFileService.create([{ resource: model.resource, value: 'Foo' }]); assert.ok(!accessor.textFileService.isDirty(model.resource)); assert.strictEqual(eventCounter, 2); - - disposable1.dispose(); - disposable2.dispose(); }); test('Filename Suggestion - Suggest prefix only when there are no relevant extensions', () => { - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: 'plumbus0', extensions: ['.one', '.two'] - }); + })); const suggested = accessor.textFileService.suggestFilename('shleem', 'Untitled-1'); assert.strictEqual(suggested, 'Untitled-1'); - registration.dispose(); }); test('Filename Suggestion - Suggest prefix with first extension', () => { - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: 'plumbus1', extensions: ['.shleem', '.gazorpazorp'], filenames: ['plumbus'] - }); + })); const suggested = accessor.textFileService.suggestFilename('plumbus1', 'Untitled-1'); assert.strictEqual(suggested, 'Untitled-1.shleem'); - registration.dispose(); }); test('Filename Suggestion - Preserve extension if it matchers', () => { - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: 'plumbus2', extensions: ['.shleem', '.gazorpazorp'], - }); + })); const suggested = accessor.textFileService.suggestFilename('plumbus2', 'Untitled-1.gazorpazorp'); assert.strictEqual(suggested, 'Untitled-1.gazorpazorp'); - registration.dispose(); }); test('Filename Suggestion - Rewrite extension according to language', () => { - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: 'plumbus2', extensions: ['.shleem', '.gazorpazorp'], - }); + })); const suggested = accessor.textFileService.suggestFilename('plumbus2', 'Untitled-1.foobar'); assert.strictEqual(suggested, 'Untitled-1.shleem'); - registration.dispose(); }); test('Filename Suggestion - Suggest filename if there are no extensions', () => { - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: 'plumbus2', filenames: ['plumbus', 'shleem', 'gazorpazorp'] - }); + })); const suggested = accessor.textFileService.suggestFilename('plumbus2', 'Untitled-1'); assert.strictEqual(suggested, 'plumbus'); - registration.dispose(); }); test('Filename Suggestion - Preserve filename if it matches', () => { - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: 'plumbus2', filenames: ['plumbus', 'shleem', 'gazorpazorp'] - }); + })); const suggested = accessor.textFileService.suggestFilename('plumbus2', 'gazorpazorp'); assert.strictEqual(suggested, 'gazorpazorp'); - registration.dispose(); }); test('Filename Suggestion - Rewrites filename according to language', () => { - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: 'plumbus2', filenames: ['plumbus', 'shleem', 'gazorpazorp'] - }); + })); const suggested = accessor.textFileService.suggestFilename('plumbus2', 'foobar'); assert.strictEqual(suggested, 'plumbus'); - registration.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts index 94864bb3094..f3b05c01014 100644 --- a/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts +++ b/src/vs/workbench/services/textfile/test/electron-sandbox/nativeTextFileService.io.test.ts @@ -22,6 +22,7 @@ import { WorkingCopyService } from 'vs/workbench/services/workingCopy/common/wor import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { TestInMemoryFileSystemProvider } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestNativeTextFileServiceWithEncodingOverrides, workbenchInstantiationService } from 'vs/workbench/test/electron-sandbox/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Files - NativeTextFileService i/o', function () { const disposables = new DisposableStore(); @@ -104,4 +105,6 @@ suite('Files - NativeTextFileService i/o', function () { return null; // ignore errors (like file not found) } } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts index 4ccf202e643..f38d12fc2bb 100644 --- a/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts +++ b/src/vs/workbench/services/textmodelResolver/test/browser/textModelResolverService.test.ts @@ -10,7 +10,7 @@ import { TextResourceEditorInput } from 'vs/workbench/common/editor/textResource import { TextResourceEditorModel } from 'vs/workbench/common/editor/textResourceEditorModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { workbenchInstantiationService, TestServiceAccessor, ITestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; @@ -37,7 +37,7 @@ suite('Workbench - TextModelResolverService', () => { }); test('resolve resource', async () => { - const disposable = accessor.textModelResolverService.registerTextModelContentProvider('test', { + disposables.add(accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: async function (resource: URI): Promise { if (resource.scheme === 'test') { const modelContent = 'Hello Test'; @@ -48,12 +48,12 @@ suite('Workbench - TextModelResolverService', () => { return null; } - }); + })); const resource = URI.from({ scheme: 'test', authority: null!, path: 'thePath' }); const input = instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, undefined); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.ok(model); assert.strictEqual(snapshotToString(((model as TextResourceEditorModel).createSnapshot()!)), 'Hello Test'); let disposed = false; @@ -67,11 +67,10 @@ suite('Workbench - TextModelResolverService', () => { await disposedPromise; assert.strictEqual(disposed, true); - disposable.dispose(); }); test('resolve file', async function () { - const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); + const textModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(textModel.resource, textModel); await textModel.resolve(); @@ -95,7 +94,7 @@ suite('Workbench - TextModelResolverService', () => { }); test('resolved dirty file eventually disposes', async function () { - const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); + const textModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(textModel.resource, textModel); await textModel.resolve(); @@ -120,7 +119,7 @@ suite('Workbench - TextModelResolverService', () => { }); test('resolved dirty file does not dispose when new reference created', async function () { - const textModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined); + const textModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file_resolver.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(textModel.resource, textModel); await textModel.resolve(); @@ -153,8 +152,8 @@ suite('Workbench - TextModelResolverService', () => { test('resolve untitled', async () => { const service = accessor.untitledTextEditorService; - const untitledModel = service.create(); - const input = instantiationService.createInstance(UntitledTextEditorInput, untitledModel); + const untitledModel = disposables.add(service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, untitledModel)); await input.resolve(); const ref = await accessor.textModelResolverService.createModelReference(input.resource); @@ -171,15 +170,15 @@ suite('Workbench - TextModelResolverService', () => { let resolveModel!: Function; const waitForIt = new Promise(resolve => resolveModel = resolve); - const disposable = accessor.textModelResolverService.registerTextModelContentProvider('test', { + disposables.add(accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: async (resource: URI): Promise => { await waitForIt; const modelContent = 'Hello Test'; const languageSelection = accessor.languageService.createById('json'); - return accessor.modelService.createModel(modelContent, languageSelection, resource); + return disposables.add(accessor.modelService.createModel(modelContent, languageSelection, resource)); } - }); + })); const uri = URI.from({ scheme: 'test', authority: null!, path: 'thePath' }); @@ -200,12 +199,12 @@ suite('Workbench - TextModelResolverService', () => { modelRef1.dispose(); assert(!textModel.isDisposed(), 'the text model should still not be disposed'); - const p1 = new Promise(resolve => textModel.onWillDispose(resolve)); + const p1 = new Promise(resolve => disposables.add(textModel.onWillDispose(resolve))); modelRef2.dispose(); await p1; assert(textModel.isDisposed(), 'the text model should finally be disposed'); - - disposable.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts index 8a0c6dc8529..c70d755430b 100644 --- a/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts +++ b/src/vs/workbench/services/untitled/test/browser/untitledTextEditor.test.ts @@ -21,6 +21,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { isReadable, isReadableStream } from 'vs/base/common/stream'; import { readableToBuffer, streamToBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; import { LanguageDetectionLanguageEventSource } from 'vs/workbench/services/languageDetection/common/languageDetectionWorkerService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Untitled text editors', () => { @@ -134,13 +135,13 @@ suite('Untitled text editors', () => { const file = URI.file(join('C:\\', '/foo/file.txt')); let onDidChangeDirtyModel: IUntitledTextEditorModel | undefined = undefined; - const listener = service.onDidChangeDirty(model => { + disposables.add(service.onDidChangeDirty(model => { onDidChangeDirtyModel = model; - }); + })); - const model = service.create({ associatedResource: file }); + const model = disposables.add(service.create({ associatedResource: file })); assert.ok(accessor.untitledTextEditorService.isUntitledWithAssociatedResource(model.resource)); - const untitled = instantiationService.createInstance(UntitledTextEditorInput, model); + const untitled = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model)); assert.ok(untitled.isDirty()); assert.strictEqual(model, onDidChangeDirtyModel); @@ -148,32 +149,28 @@ suite('Untitled text editors', () => { assert.ok(resolvedModel.hasAssociatedFilePath); assert.strictEqual(untitled.isDirty(), true); - - untitled.dispose(); - listener.dispose(); }); test('no longer dirty when content gets empty (not with associated resource)', async () => { const service = accessor.untitledTextEditorService; const workingCopyService = accessor.workingCopyService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); // dirty - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); model.textEditorModel?.setValue('foo bar'); assert.ok(model.isDirty()); assert.ok(workingCopyService.isDirty(model.resource, model.typeId)); model.textEditorModel?.setValue(''); assert.ok(!model.isDirty()); assert.ok(!workingCopyService.isDirty(model.resource, model.typeId)); - input.dispose(); - model.dispose(); }); test('via create options', async () => { const service = accessor.untitledTextEditorService; - const model1 = await instantiationService.createInstance(UntitledTextEditorInput, service.create()).resolve(); + const input1 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); + const model1 = disposables.add(await input1.resolve()); model1.textEditorModel!.setValue('foo bar'); assert.ok(model1.isDirty()); @@ -181,47 +178,42 @@ suite('Untitled text editors', () => { model1.textEditorModel!.setValue(''); assert.ok(!model1.isDirty()); - const model2 = await instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Hello World' })).resolve(); + const input2 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Hello World' }))); + const model2 = disposables.add(await input2.resolve()); assert.strictEqual(snapshotToString(model2.createSnapshot()!), 'Hello World'); - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, disposables.add(service.create()))); - const model3 = await instantiationService.createInstance(UntitledTextEditorInput, service.create({ untitledResource: input.resource })).resolve(); + const input3 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ untitledResource: input.resource }))); + const model3 = disposables.add(await input3.resolve()); assert.strictEqual(model3.resource.toString(), input.resource.toString()); const file = URI.file(join('C:\\', '/foo/file44.txt')); - const model4 = await instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: file })).resolve(); + const input4 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: file }))); + const model4 = disposables.add(await input4.resolve()); assert.ok(model4.hasAssociatedFilePath); assert.ok(model4.isDirty()); - - model1.dispose(); - model2.dispose(); - model3.dispose(); - model4.dispose(); - input.dispose(); }); test('associated path remains dirty when content gets empty', async () => { const service = accessor.untitledTextEditorService; const file = URI.file(join('C:\\', '/foo/file.txt')); - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: file })); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ associatedResource: file }))); // dirty - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); model.textEditorModel?.setValue('foo bar'); assert.ok(model.isDirty()); model.textEditorModel?.setValue(''); assert.ok(model.isDirty()); - input.dispose(); - model.dispose(); }); test('initial content is dirty', async () => { const service = accessor.untitledTextEditorService; const workingCopyService = accessor.workingCopyService; - const untitled = instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Hello World' })); + const untitled = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Hello World' }))); assert.ok(untitled.isDirty()); const backup = (await untitled.model.backup(CancellationToken.None)).content; @@ -236,12 +228,9 @@ suite('Untitled text editors', () => { } // dirty - const model = await untitled.resolve(); + const model = disposables.add(await untitled.resolve()); assert.ok(model.isDirty()); assert.strictEqual(workingCopyService.dirtyCount, 1); - - untitled.dispose(); - model.dispose(); }); test('created with files.defaultLanguage setting', () => { @@ -250,13 +239,11 @@ suite('Untitled text editors', () => { config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); const service = accessor.untitledTextEditorService; - const input = service.create(); + const input = disposables.add(service.create()); assert.strictEqual(input.getLanguageId(), defaultLanguage); config.setUserConfiguration('files', { 'defaultLanguage': undefined }); - - input.dispose(); }); test('created with files.defaultLanguage setting (${activeEditorLanguage})', async () => { @@ -266,14 +253,12 @@ suite('Untitled text editors', () => { accessor.editorService.activeTextEditorLanguageId = 'typescript'; const service = accessor.untitledTextEditorService; - const model = service.create(); + const model = disposables.add(service.create()); assert.strictEqual(model.getLanguageId(), 'typescript'); config.setUserConfiguration('files', { 'defaultLanguage': undefined }); accessor.editorService.activeTextEditorLanguageId = undefined; - - model.dispose(); }); test('created with language overrides files.defaultLanguage setting', () => { @@ -283,94 +268,81 @@ suite('Untitled text editors', () => { config.setUserConfiguration('files', { 'defaultLanguage': defaultLanguage }); const service = accessor.untitledTextEditorService; - const input = service.create({ languageId: language }); + const input = disposables.add(service.create({ languageId: language })); assert.strictEqual(input.getLanguageId(), language); config.setUserConfiguration('files', { 'defaultLanguage': undefined }); - - input.dispose(); }); test('can change language afterwards', async () => { const languageId = 'untitled-input-test'; - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: languageId, - }); + })); const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create({ languageId: languageId })); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ languageId: languageId }))); assert.strictEqual(input.getLanguageId(), languageId); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.strictEqual(model.getLanguageId(), languageId); input.setLanguageId(PLAINTEXT_LANGUAGE_ID); assert.strictEqual(input.getLanguageId(), PLAINTEXT_LANGUAGE_ID); - - input.dispose(); - model.dispose(); - registration.dispose(); }); test('remembers that language was set explicitly', async () => { const language = 'untitled-input-test'; - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: language, - }); + })); const service = accessor.untitledTextEditorService; - const model = service.create(); - const input = instantiationService.createInstance(UntitledTextEditorInput, model); + const model = disposables.add(service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model)); assert.ok(!input.model.hasLanguageSetExplicitly); input.setLanguageId(PLAINTEXT_LANGUAGE_ID); assert.ok(input.model.hasLanguageSetExplicitly); assert.strictEqual(input.getLanguageId(), PLAINTEXT_LANGUAGE_ID); - - input.dispose(); - model.dispose(); - registration.dispose(); }); // Issue #159202 test('remembers that language was set explicitly if set by another source (i.e. ModelService)', async () => { const language = 'untitled-input-test'; - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: language, - }); + })); const service = accessor.untitledTextEditorService; - const model = service.create(); - const input = instantiationService.createInstance(UntitledTextEditorInput, model); - await input.resolve(); + const model = disposables.add(service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model)); + disposables.add(await input.resolve()); assert.ok(!input.model.hasLanguageSetExplicitly); model.textEditorModel!.setLanguage(accessor.languageService.createById(language)); assert.ok(input.model.hasLanguageSetExplicitly); assert.strictEqual(model.getLanguageId(), language); - - model.dispose(); - registration.dispose(); }); test('Language is not set explicitly if set by language detection source', async () => { const language = 'untitled-input-test'; - const registration = accessor.languageService.registerLanguage({ + disposables.add(accessor.languageService.registerLanguage({ id: language, - }); + })); const service = accessor.untitledTextEditorService; - const model = service.create(); - const input = instantiationService.createInstance(UntitledTextEditorInput, model); + const model = disposables.add(service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, model)); await input.resolve(); assert.ok(!input.model.hasLanguageSetExplicitly); @@ -381,61 +353,54 @@ suite('Untitled text editors', () => { assert.ok(!input.model.hasLanguageSetExplicitly); assert.strictEqual(model.getLanguageId(), language); - - model.dispose(); - registration.dispose(); }); test('service#onDidChangeEncoding', async () => { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - service.onDidChangeEncoding(model => { + disposables.add(service.onDidChangeEncoding(model => { counter++; assert.strictEqual(model.resource.toString(), input.resource.toString()); - }); + })); // encoding - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); await model.setEncoding('utf16'); assert.strictEqual(counter, 1); - input.dispose(); - model.dispose(); }); test('service#onDidChangeLabel', async () => { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - service.onDidChangeLabel(model => { + disposables.add(service.onDidChangeLabel(model => { counter++; assert.strictEqual(model.resource.toString(), input.resource.toString()); - }); + })); // label - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); model.textEditorModel?.setValue('Foo Bar'); assert.strictEqual(counter, 1); - input.dispose(); - model.dispose(); }); test('service#onWillDispose', async () => { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - service.onWillDispose(model => { + disposables.add(service.onWillDispose(model => { counter++; assert.strictEqual(model.resource.toString(), input.resource.toString()); - }); + })); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.strictEqual(counter, 0); model.dispose(); assert.strictEqual(counter, 1); @@ -443,9 +408,9 @@ suite('Untitled text editors', () => { test('service#getValue', async () => { - // This function is used for the untitledocumentData API const service = accessor.untitledTextEditorService; - const model1 = await instantiationService.createInstance(UntitledTextEditorInput, service.create()).resolve(); + const input1 = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); + const model1 = disposables.add(await input1.resolve()); model1.textEditorModel!.setValue('foo bar'); assert.strictEqual(service.getValue(model1.resource), 'foo bar'); @@ -457,12 +422,12 @@ suite('Untitled text editors', () => { test('model#onDidChangeContent', async function () { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - const model = await input.resolve(); - model.onDidChangeContent(() => counter++); + const model = disposables.add(await input.resolve()); + disposables.add(model.onDidChangeContent(() => counter++)); model.textEditorModel?.setValue('foo'); @@ -476,19 +441,16 @@ suite('Untitled text editors', () => { model.textEditorModel?.setValue('foo'); assert.strictEqual(counter, 4, 'Dirty model should trigger event'); - - input.dispose(); - model.dispose(); }); test('model#onDidRevert and input disposed when reverted', async function () { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - const model = await input.resolve(); - model.onDidRevert(() => counter++); + const model = disposables.add(await input.resolve()); + disposables.add(model.onDidRevert(() => counter++)); model.textEditorModel?.setValue('foo'); @@ -500,12 +462,12 @@ suite('Untitled text editors', () => { test('model#onDidChangeName and input name', async function () { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - let model = await input.resolve(); - model.onDidChangeName(() => counter++); + let model = disposables.add(await input.resolve()); + disposables.add(model.onDidChangeName(() => counter++)); model.textEditorModel?.setValue('foo'); assert.strictEqual(input.getName(), 'foo'); @@ -571,23 +533,20 @@ suite('Untitled text editors', () => { input.dispose(); model.dispose(); - const inputWithContents = instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Foo' })); - model = await inputWithContents.resolve(); + const inputWithContents = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create({ initialValue: 'Foo' }))); + model = disposables.add(await inputWithContents.resolve()); assert.strictEqual(inputWithContents.getName(), 'Foo'); - - inputWithContents.dispose(); - model.dispose(); }); test('model#onDidChangeDirty', async function () { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - const model = await input.resolve(); - model.onDidChangeDirty(() => counter++); + const model = disposables.add(await input.resolve()); + disposables.add(model.onDidChangeDirty(() => counter++)); model.textEditorModel?.setValue('foo'); @@ -595,19 +554,16 @@ suite('Untitled text editors', () => { model.textEditorModel?.setValue('bar'); assert.strictEqual(counter, 1, 'Another change does not fire event'); - - input.dispose(); - model.dispose(); }); test('model#onDidChangeEncoding', async function () { const service = accessor.untitledTextEditorService; - const input = instantiationService.createInstance(UntitledTextEditorInput, service.create()); + const input = disposables.add(instantiationService.createInstance(UntitledTextEditorInput, service.create())); let counter = 0; - const model = await input.resolve(); - model.onDidChangeEncoding(() => counter++); + const model = disposables.add(await input.resolve()); + disposables.add(model.onDidChangeEncoding(() => counter++)); await model.setEncoding('utf16'); @@ -615,8 +571,7 @@ suite('Untitled text editors', () => { await model.setEncoding('utf16'); assert.strictEqual(counter, 1, 'Another change to same encoding does not fire event'); - - input.dispose(); - model.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts index ee95011f41f..13bdd16574b 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyBackupTracker.test.ts @@ -11,7 +11,7 @@ import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editor import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; import { IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopy'; @@ -19,7 +19,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -import { createEditorPart, InMemoryTestWorkingCopyBackupService, registerTestResourceEditor, TestServiceAccessor, toTypedWorkingCopyId, toUntypedWorkingCopyId, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { createEditorPart, InMemoryTestWorkingCopyBackupService, registerTestResourceEditor, TestServiceAccessor, toTypedWorkingCopyId, toUntypedWorkingCopyId, workbenchInstantiationService, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; import { CancellationToken } from 'vs/base/common/cancellation'; import { timeout } from 'vs/base/common/async'; @@ -39,13 +39,7 @@ suite('WorkingCopyBackupTracker (browser)', function () { }); teardown(async () => { - for (const copy of accessor.workingCopyService.workingCopies) { - await copy.revert(); - } - - for (const group of accessor.editorGroupService.groups) { - await group.closeAllEditors(); - } + await workbenchTeardown(accessor.instantiationService); disposables.clear(); }); @@ -114,9 +108,8 @@ suite('WorkingCopyBackupTracker (browser)', function () { async function untitledBackupTest(untitled: IUntitledTextResourceEditorInput = { resource: undefined }): Promise { const { accessor, workingCopyBackupService } = await createTracker(); - const untitledTextEditor = (await accessor.editorService.openEditor(untitled))?.input as UntitledTextEditorInput; - - const untitledTextModel = await untitledTextEditor.resolve(); + const untitledTextEditor = disposables.add((await accessor.editorService.openEditor(untitled))?.input as UntitledTextEditorInput); + const untitledTextModel = disposables.add(await untitledTextEditor.resolve()); if (!untitled?.contents) { untitledTextModel.textEditorModel?.setValue('Super Good'); @@ -307,7 +300,7 @@ suite('WorkingCopyBackupTracker (browser)', function () { return false; }, createEditor: workingCopy => { - return accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + return disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); } }); @@ -319,7 +312,7 @@ suite('WorkingCopyBackupTracker (browser)', function () { return false; }, createEditor: workingCopy => { - return accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' })); + return disposables.add(accessor.instantiationService.createInstance(TestUntitledTextEditorInput, accessor.untitledTextEditorService.create({ initialValue: 'foo' }))); } }); @@ -382,4 +375,6 @@ suite('WorkingCopyBackupTracker (browser)', function () { } } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts index 14e3b835991..e63425a837c 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/workingCopyFileService.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { workbenchInstantiationService, TestServiceAccessor, ITestTextFileEditorModelManager } from 'vs/workbench/test/browser/workbenchTestServices'; import { URI } from 'vs/base/common/uri'; import { FileOperation } from 'vs/platform/files/common/files'; @@ -171,12 +171,12 @@ suite('WorkingCopyFileService', () => { }); test('registerWorkingCopyProvider', async function () { - const model1 = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-1.txt'), 'utf8', undefined); + const model1: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file-1.txt'), 'utf8', undefined)); (accessor.textFileService.files).add(model1.resource, model1); await model1.resolve(); model1.textEditorModel!.setValue('foo'); - const testWorkingCopy = new TestWorkingCopy(toResource.call(this, '/path/file-2.txt'), true); + const testWorkingCopy: TestWorkingCopy = disposables.add(new TestWorkingCopy(toResource.call(this, '/path/file-2.txt'), true)); const registration = accessor.workingCopyFileService.registerWorkingCopyProvider(() => { return [model1, testWorkingCopy]; }); @@ -191,8 +191,6 @@ suite('WorkingCopyFileService', () => { dirty = accessor.workingCopyFileService.getDirty(model1.resource); assert.strictEqual(dirty.length, 1, 'Should have unregistered our provider'); assert.strictEqual(dirty[0], model1); - - model1.dispose(); }); test('createFolder', async function () { @@ -201,7 +199,7 @@ suite('WorkingCopyFileService', () => { const resource = toResource.call(this, '/path/folder'); - const participant = accessor.workingCopyFileService.addFileOperationParticipant({ + disposables.add(accessor.workingCopyFileService.addFileOperationParticipant({ participate: async (files, operation) => { assert.strictEqual(files.length, 1); const file = files[0]; @@ -209,45 +207,41 @@ suite('WorkingCopyFileService', () => { assert.strictEqual(operation, FileOperation.CREATE); eventCounter++; } - }); + })); - const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { + disposables.add(accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { assert.strictEqual(e.files.length, 1); const file = e.files[0]; assert.strictEqual(file.target.toString(), resource.toString()); assert.strictEqual(e.operation, FileOperation.CREATE); correlationId = e.correlationId; eventCounter++; - }); + })); - const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + disposables.add(accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { assert.strictEqual(e.files.length, 1); const file = e.files[0]; assert.strictEqual(file.target.toString(), resource.toString()); assert.strictEqual(e.operation, FileOperation.CREATE); assert.strictEqual(e.correlationId, correlationId); eventCounter++; - }); + })); await accessor.workingCopyFileService.createFolder([{ resource }], CancellationToken.None); assert.strictEqual(eventCounter, 3); - - participant.dispose(); - listener1.dispose(); - listener2.dispose(); }); test('cancellation of participants', async function () { const resource = toResource.call(this, '/path/folder'); let canceled = false; - const participant = accessor.workingCopyFileService.addFileOperationParticipant({ + disposables.add(accessor.workingCopyFileService.addFileOperationParticipant({ participate: async (files, operation, info, t, token) => { await timeout(0); canceled = token.isCancellationRequested; } - }); + })); // Create let cts = new CancellationTokenSource(); @@ -288,8 +282,6 @@ suite('WorkingCopyFileService', () => { await promise; assert.strictEqual(canceled, true); canceled = false; - - participant.dispose(); }); async function testEventsMoveOrCopy(files: ICopyOperation[], move?: boolean): Promise { @@ -490,7 +482,7 @@ suite('WorkingCopyFileService', () => { let eventCounter = 0; let correlationId: number | undefined = undefined; - const participant = accessor.workingCopyFileService.addFileOperationParticipant({ + disposables.add(accessor.workingCopyFileService.addFileOperationParticipant({ participate: async (files, operation) => { assert.strictEqual(files.length, 1); const file = files[0]; @@ -498,34 +490,32 @@ suite('WorkingCopyFileService', () => { assert.strictEqual(operation, FileOperation.CREATE); eventCounter++; } - }); + })); - const listener1 = accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { + disposables.add(accessor.workingCopyFileService.onWillRunWorkingCopyFileOperation(e => { assert.strictEqual(e.files.length, 1); const file = e.files[0]; assert.strictEqual(file.target.toString(), model.resource.toString()); assert.strictEqual(e.operation, FileOperation.CREATE); correlationId = e.correlationId; eventCounter++; - }); + })); - const listener2 = accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { + disposables.add(accessor.workingCopyFileService.onDidRunWorkingCopyFileOperation(e => { assert.strictEqual(e.files.length, 1); const file = e.files[0]; assert.strictEqual(file.target.toString(), model.resource.toString()); assert.strictEqual(e.operation, FileOperation.CREATE); assert.strictEqual(e.correlationId, correlationId); eventCounter++; - }); + })); await accessor.workingCopyFileService.create([{ resource, contents }], CancellationToken.None); assert.ok(!accessor.workingCopyService.isDirty(model.resource)); model.dispose(); assert.strictEqual(eventCounter, 3); - - participant.dispose(); - listener1.dispose(); - listener2.dispose(); } + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/part.test.ts b/src/vs/workbench/test/browser/part.test.ts index 20c15a41f63..b42ade588b8 100644 --- a/src/vs/workbench/test/browser/part.test.ts +++ b/src/vs/workbench/test/browser/part.test.ts @@ -11,9 +11,13 @@ import { append, $, hide } from 'vs/base/browser/dom'; import { TestLayoutService } from 'vs/workbench/test/browser/workbenchTestServices'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Workbench parts', () => { + const disposables = new DisposableStore(); + class SimplePart extends Part { minimumWidth: number = 50; @@ -33,7 +37,7 @@ suite('Workbench parts', () => { class MyPart extends SimplePart { constructor(private expectedParent: HTMLElement) { - super('myPart', { hasTitle: true }, new TestThemeService(), new TestStorageService(), new TestLayoutService()); + super('myPart', { hasTitle: true }, new TestThemeService(), disposables.add(new TestStorageService()), new TestLayoutService()); } protected override createTitleArea(parent: HTMLElement): HTMLElement { @@ -58,7 +62,7 @@ suite('Workbench parts', () => { class MyPart2 extends SimplePart { constructor() { - super('myPart2', { hasTitle: true }, new TestThemeService(), new TestStorageService(), new TestLayoutService()); + super('myPart2', { hasTitle: true }, new TestThemeService(), disposables.add(new TestStorageService()), new TestLayoutService()); } protected override createTitleArea(parent: HTMLElement): HTMLElement { @@ -83,7 +87,7 @@ suite('Workbench parts', () => { class MyPart3 extends SimplePart { constructor() { - super('myPart2', { hasTitle: false }, new TestThemeService(), new TestStorageService(), new TestLayoutService()); + super('myPart2', { hasTitle: false }, new TestThemeService(), disposables.add(new TestStorageService()), new TestLayoutService()); } protected override createTitleArea(parent: HTMLElement): HTMLElement { @@ -111,6 +115,7 @@ suite('Workbench parts', () => { teardown(() => { document.body.removeChild(fixture); + disposables.clear(); }); test('Creation', () => { @@ -118,7 +123,7 @@ suite('Workbench parts', () => { document.getElementById(fixtureId)!.appendChild(b); hide(b); - let part = new MyPart(b); + let part = disposables.add(new MyPart(b)); part.create(b); assert.strictEqual(part.getId(), 'myPart'); @@ -132,7 +137,7 @@ suite('Workbench parts', () => { part.testSaveState(); // Re-Create to assert memento contents - part = new MyPart(b); + part = disposables.add(new MyPart(b)); memento = part.testGetMemento(StorageScope.PROFILE, StorageTarget.MACHINE); assert(memento); @@ -144,7 +149,7 @@ suite('Workbench parts', () => { delete memento.bar; part.testSaveState(); - part = new MyPart(b); + part = disposables.add(new MyPart(b)); memento = part.testGetMemento(StorageScope.PROFILE, StorageTarget.MACHINE); assert(memento); assert.strictEqual(isEmptyObject(memento), true); @@ -155,7 +160,7 @@ suite('Workbench parts', () => { document.getElementById(fixtureId)!.appendChild(b); hide(b); - const part = new MyPart2(); + const part = disposables.add(new MyPart2()); part.create(b); assert(document.getElementById('myPart.title')); @@ -167,10 +172,12 @@ suite('Workbench parts', () => { document.getElementById(fixtureId)!.appendChild(b); hide(b); - const part = new MyPart3(); + const part = disposables.add(new MyPart3()); part.create(b); assert(!document.getElementById('myPart.title')); assert(document.getElementById('myPart.content')); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts index d94e0412166..be643126620 100644 --- a/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/diffEditorInput.test.ts @@ -10,6 +10,7 @@ import { workbenchInstantiationService } from 'vs/workbench/test/browser/workben import { EditorResourceAccessor, isDiffEditorInput, isResourceDiffEditorInput, isResourceSideBySideEditorInput, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Diff editor input', () => { @@ -46,17 +47,17 @@ suite('Diff editor input', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); let counter = 0; - const input = new MyEditorInput(); - input.onWillDispose(() => { + const input = disposables.add(new MyEditorInput()); + disposables.add(input.onWillDispose(() => { assert(true); counter++; - }); + })); - const otherInput = new MyEditorInput(); - otherInput.onWillDispose(() => { + const otherInput = disposables.add(new MyEditorInput()); + disposables.add(otherInput.onWillDispose(() => { assert(true); counter++; - }); + })); const diffInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); @@ -68,7 +69,6 @@ suite('Diff editor input', () => { assert(diffInput.matches(diffInput)); assert(!diffInput.matches(otherInput)); - diffInput.dispose(); assert.strictEqual(counter, 0); }); @@ -76,8 +76,8 @@ suite('Diff editor input', () => { test('toUntyped', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); - const input = new MyEditorInput(URI.file('foo/bar1')); - const otherInput = new MyEditorInput(URI.file('foo/bar2')); + const input = disposables.add(new MyEditorInput(URI.file('foo/bar1'))); + const otherInput = disposables.add(new MyEditorInput(URI.file('foo/bar2'))); const diffInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); @@ -91,27 +91,29 @@ suite('Diff editor input', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); let counter = 0; - let input = new MyEditorInput(); - let otherInput = new MyEditorInput(); + let input = disposables.add(new MyEditorInput()); + let otherInput = disposables.add(new MyEditorInput()); - const diffInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); - diffInput.onWillDispose(() => { + const diffInput = disposables.add(instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined)); + disposables.add(diffInput.onWillDispose(() => { counter++; assert(true); - }); + })); input.dispose(); - input = new MyEditorInput(); - otherInput = new MyEditorInput(); + input = disposables.add(new MyEditorInput()); + otherInput = disposables.add(new MyEditorInput()); - const diffInput2 = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); - diffInput2.onWillDispose(() => { + const diffInput2 = disposables.add(instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined)); + disposables.add(diffInput2.onWillDispose(() => { counter++; assert(true); - }); + })); otherInput.dispose(); assert.strictEqual(counter, 2); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts index 474fe6c56bb..b6ece8430e7 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorDiffModel.test.ts @@ -12,6 +12,7 @@ import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench import { ITextModel } from 'vs/editor/common/model'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('TextDiffEditorModel', () => { @@ -29,24 +30,24 @@ suite('TextDiffEditorModel', () => { }); test('basics', async () => { - const dispose = accessor.textModelResolverService.registerTextModelContentProvider('test', { + disposables.add(accessor.textModelResolverService.registerTextModelContentProvider('test', { provideTextContent: async function (resource: URI): Promise { if (resource.scheme === 'test') { const modelContent = 'Hello Test'; const languageSelection = accessor.languageService.createById('json'); - return accessor.modelService.createModel(modelContent, languageSelection, resource); + return disposables.add(accessor.modelService.createModel(modelContent, languageSelection, resource)); } return null; } - }); + })); - const input = instantiationService.createInstance(TextResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name', 'description', undefined, undefined); - const otherInput = instantiationService.createInstance(TextResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name2', 'description', undefined, undefined); - const diffInput = instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined); + const input = disposables.add(instantiationService.createInstance(TextResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name', 'description', undefined, undefined)); + const otherInput = disposables.add(instantiationService.createInstance(TextResourceEditorInput, URI.from({ scheme: 'test', authority: null!, path: 'thePath' }), 'name2', 'description', undefined, undefined)); + const diffInput = disposables.add(instantiationService.createInstance(DiffEditorInput, 'name', 'description', input, otherInput, undefined)); - let model = await diffInput.resolve() as TextDiffEditorModel; + let model = disposables.add(await diffInput.resolve() as TextDiffEditorModel); assert(model); assert(model instanceof TextDiffEditorModel); @@ -55,13 +56,13 @@ suite('TextDiffEditorModel', () => { assert(diffEditorModel.original); assert(diffEditorModel.modified); - model = await diffInput.resolve() as TextDiffEditorModel; + model = disposables.add(await diffInput.resolve() as TextDiffEditorModel); assert(model.isResolved()); assert(diffEditorModel !== model.textDiffEditorModel); diffInput.dispose(); assert(!model.textDiffEditorModel); - - dispose.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts index 3467cdcd507..61be5f1243c 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts @@ -20,11 +20,12 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { TestContextService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { isEqual } from 'vs/base/common/resources'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('EditorGroupModel', () => { @@ -40,8 +41,8 @@ suite('EditorGroupModel', () => { testInstService = new TestInstantiationService(); } const inst = testInstService; - inst.stub(IStorageService, new TestStorageService()); - inst.stub(ILifecycleService, new TestLifecycleService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); + inst.stub(ILifecycleService, disposables.add(new TestLifecycleService())); inst.stub(IWorkspaceContextService, new TestContextService()); inst.stub(ITelemetryService, NullTelemetryService); @@ -53,7 +54,15 @@ suite('EditorGroupModel', () => { } function createEditorGroupModel(serialized?: ISerializedEditorGroupModel): EditorGroupModel { - return inst().createInstance(EditorGroupModel, serialized); + const group = disposables.add(inst().createInstance(EditorGroupModel, serialized)); + + disposables.add(toDisposable(() => { + for (const editor of group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)) { + group.closeEditor(editor); + } + })); + + return group; } function closeAllEditors(group: EditorGroupModel): void { @@ -119,7 +128,7 @@ suite('EditorGroupModel', () => { disposed: [] }; - group.onDidModelChange(e => { + disposables.add(group.onDidModelChange(e => { if (e.kind === GroupModelChangeKind.GROUP_LOCKED) { groupEvents.locked.push(group.id); return; @@ -170,7 +179,7 @@ suite('EditorGroupModel', () => { } break; } - }); + })); return groupEvents; } @@ -251,10 +260,10 @@ suite('EditorGroupModel', () => { function input(id = String(index++), nonSerializable?: boolean, resource?: URI): EditorInput { if (resource) { - return new TestFileEditorInput(id, resource); + return disposables.add(new TestFileEditorInput(id, resource)); } - return nonSerializable ? new NonSerializableTestEditorInput(id) : new TestEditorInput(id); + return nonSerializable ? disposables.add(new NonSerializableTestEditorInput(id)) : disposables.add(new TestEditorInput(id)); } interface ISerializedTestInput { @@ -290,7 +299,7 @@ suite('EditorGroupModel', () => { const testInput: ISerializedTestInput = JSON.parse(serializedEditorInput); - return new TestEditorInput(testInput.id); + return disposables.add(new TestEditorInput(testInput.id)); } } @@ -330,7 +339,7 @@ suite('EditorGroupModel', () => { group.lock(true); assert.strictEqual(group.isLocked, true); - const clone = group.clone(); + const clone = disposables.add(group.clone()); assert.notStrictEqual(group.id, clone.id); assert.strictEqual(clone.count, 3); assert.strictEqual(clone.isLocked, false); // locking does not clone over @@ -361,8 +370,8 @@ suite('EditorGroupModel', () => { test('isActive - untyped', () => { const group = createEditorGroupModel(); - const input = new TestFileEditorInput('testInput', URI.file('fake')); - const input2 = new TestFileEditorInput('testInput2', URI.file('fake2')); + const input = disposables.add(new TestFileEditorInput('testInput', URI.file('fake'))); + const input2 = disposables.add(new TestFileEditorInput('testInput2', URI.file('fake2'))); const untypedInput = { resource: URI.file('/fake'), options: { override: 'testInput' } }; const untypedNonActiveInput = { resource: URI.file('/fake2'), options: { override: 'testInput2' } }; @@ -378,8 +387,8 @@ suite('EditorGroupModel', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); const group = createEditorGroupModel(); - const input1 = new TestFileEditorInput('testInput', URI.file('fake1')); - const input2 = new TestFileEditorInput('testInput', URI.file('fake2')); + const input1 = disposables.add(new TestFileEditorInput('testInput', URI.file('fake1'))); + const input2 = disposables.add(new TestFileEditorInput('testInput', URI.file('fake2'))); const sideBySideInputSame = instantiationService.createInstance(SideBySideEditorInput, undefined, undefined, input1, input1); const sideBySideInputDifferent = instantiationService.createInstance(SideBySideEditorInput, undefined, undefined, input1, input2); @@ -406,7 +415,7 @@ suite('EditorGroupModel', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); const group = createEditorGroupModel(); - const input1 = new TestFileEditorInput('testInput', URI.file('fake1')); + const input1 = disposables.add(new TestFileEditorInput('testInput', URI.file('fake1'))); const sideBySideInput = instantiationService.createInstance(SideBySideEditorInput, undefined, undefined, input1, input1); @@ -1044,8 +1053,8 @@ suite('EditorGroupModel', () => { test('Multiple Editors - Pinned and Active (DEFAULT_OPEN_EDITOR_DIRECTION = Direction.LEFT)', function () { const inst = new TestInstantiationService(); - inst.stub(IStorageService, new TestStorageService()); - inst.stub(ILifecycleService, new TestLifecycleService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); + inst.stub(ILifecycleService, disposables.add(new TestLifecycleService())); inst.stub(IWorkspaceContextService, new TestContextService()); inst.stub(ITelemetryService, NullTelemetryService); @@ -1053,7 +1062,7 @@ suite('EditorGroupModel', () => { inst.stub(IConfigurationService, config); config.setUserConfiguration('workbench', { editor: { openPositioning: 'left' } }); - const group: EditorGroupModel = inst.createInstance(EditorGroupModel, undefined); + const group: EditorGroupModel = disposables.add(inst.createInstance(EditorGroupModel, undefined)); const events = groupListener(group); @@ -1277,8 +1286,8 @@ suite('EditorGroupModel', () => { test('Multiple Editors - closing picks next to the right', function () { const inst = new TestInstantiationService(); - inst.stub(IStorageService, new TestStorageService()); - inst.stub(ILifecycleService, new TestLifecycleService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); + inst.stub(ILifecycleService, disposables.add(new TestLifecycleService())); inst.stub(IWorkspaceContextService, new TestContextService()); inst.stub(ITelemetryService, NullTelemetryService); @@ -1286,7 +1295,7 @@ suite('EditorGroupModel', () => { config.setUserConfiguration('workbench', { editor: { focusRecentEditorAfterClose: false } }); inst.stub(IConfigurationService, config); - const group = inst.createInstance(EditorGroupModel, undefined); + const group = disposables.add(inst.createInstance(EditorGroupModel, undefined)); const events = groupListener(group); const input1 = input(); @@ -1656,9 +1665,9 @@ suite('EditorGroupModel', () => { test('Single Group, Single Editor - persist', function () { const inst = new TestInstantiationService(); - inst.stub(IStorageService, new TestStorageService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); inst.stub(IWorkspaceContextService, new TestContextService()); - const lifecycle = new TestLifecycleService(); + const lifecycle = disposables.add(new TestLifecycleService()); inst.stub(ILifecycleService, lifecycle); inst.stub(ITelemetryService, NullTelemetryService); @@ -1679,7 +1688,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group.isActive(input1), true); // Create model again - should load from storage - group = inst.createInstance(EditorGroupModel, group.serialize()); + group = disposables.add(inst.createInstance(EditorGroupModel, group.serialize())); assert.strictEqual(group.count, 1); assert.strictEqual(group.activeEditor!.matches(input1), true); @@ -1691,9 +1700,9 @@ suite('EditorGroupModel', () => { test('Multiple Groups, Multiple editors - persist', function () { const inst = new TestInstantiationService(); - inst.stub(IStorageService, new TestStorageService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); inst.stub(IWorkspaceContextService, new TestContextService()); - const lifecycle = new TestLifecycleService(); + const lifecycle = disposables.add(new TestLifecycleService()); inst.stub(ILifecycleService, lifecycle); inst.stub(ITelemetryService, NullTelemetryService); @@ -1739,8 +1748,8 @@ suite('EditorGroupModel', () => { assert.strictEqual(group2.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[2].matches(g2_input2), true); // Create model again - should load from storage - group1 = inst.createInstance(EditorGroupModel, group1.serialize()); - group2 = inst.createInstance(EditorGroupModel, group2.serialize()); + group1 = disposables.add(inst.createInstance(EditorGroupModel, group1.serialize())); + group2 = disposables.add(inst.createInstance(EditorGroupModel, group2.serialize())); assert.strictEqual(group1.count, 3); assert.strictEqual(group2.count, 3); @@ -1762,9 +1771,9 @@ suite('EditorGroupModel', () => { test('Single group, multiple editors - persist (some not persistable)', function () { const inst = new TestInstantiationService(); - inst.stub(IStorageService, new TestStorageService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); inst.stub(IWorkspaceContextService, new TestContextService()); - const lifecycle = new TestLifecycleService(); + const lifecycle = disposables.add(new TestLifecycleService()); inst.stub(ILifecycleService, lifecycle); inst.stub(ITelemetryService, NullTelemetryService); @@ -1793,7 +1802,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[2].matches(serializableInput1), true); // Create model again - should load from storage - group = inst.createInstance(EditorGroupModel, group.serialize()); + group = disposables.add(inst.createInstance(EditorGroupModel, group.serialize())); assert.strictEqual(group.count, 2); assert.strictEqual(group.activeEditor!.matches(serializableInput2), true); @@ -1807,9 +1816,9 @@ suite('EditorGroupModel', () => { test('Single group, multiple editors - persist (some not persistable, sticky editors)', function () { const inst = new TestInstantiationService(); - inst.stub(IStorageService, new TestStorageService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); inst.stub(IWorkspaceContextService, new TestContextService()); - const lifecycle = new TestLifecycleService(); + const lifecycle = disposables.add(new TestLifecycleService()); inst.stub(ILifecycleService, lifecycle); inst.stub(ITelemetryService, NullTelemetryService); @@ -1833,7 +1842,7 @@ suite('EditorGroupModel', () => { assert.strictEqual(group.stickyCount, 1); // Create model again - should load from storage - group = inst.createInstance(EditorGroupModel, group.serialize()); + group = disposables.add(inst.createInstance(EditorGroupModel, group.serialize())); assert.strictEqual(group.count, 2); assert.strictEqual(group.stickyCount, 0); @@ -1843,9 +1852,9 @@ suite('EditorGroupModel', () => { test('Multiple groups, multiple editors - persist (some not persistable, causes empty group)', function () { const inst = new TestInstantiationService(); - inst.stub(IStorageService, new TestStorageService()); + inst.stub(IStorageService, disposables.add(new TestStorageService())); inst.stub(IWorkspaceContextService, new TestContextService()); - const lifecycle = new TestLifecycleService(); + const lifecycle = disposables.add(new TestLifecycleService()); inst.stub(ILifecycleService, lifecycle); inst.stub(ITelemetryService, NullTelemetryService); @@ -1868,8 +1877,8 @@ suite('EditorGroupModel', () => { group2.openEditor(nonSerializableInput); // Create model again - should load from storage - group1 = inst.createInstance(EditorGroupModel, group1.serialize()); - group2 = inst.createInstance(EditorGroupModel, group2.serialize()); + group1 = disposables.add(inst.createInstance(EditorGroupModel, group1.serialize())); + group2 = disposables.add(inst.createInstance(EditorGroupModel, group2.serialize())); assert.strictEqual(group1.count, 2); assert.strictEqual(group1.getEditors(EditorsOrder.SEQUENTIAL)[0].matches(serializableInput1), true); @@ -1937,32 +1946,32 @@ suite('EditorGroupModel', () => { group2.openEditor(input2, { pinned: true, active: true }); let dirty1Counter = 0; - group1.onDidModelChange((e) => { + disposables.add(group1.onDidModelChange((e) => { if (e.kind === GroupModelChangeKind.EDITOR_DIRTY) { dirty1Counter++; } - }); + })); let dirty2Counter = 0; - group2.onDidModelChange((e) => { + disposables.add(group2.onDidModelChange((e) => { if (e.kind === GroupModelChangeKind.EDITOR_DIRTY) { dirty2Counter++; } - }); + })); let label1ChangeCounter = 0; - group1.onDidModelChange((e) => { + disposables.add(group1.onDidModelChange((e) => { if (e.kind === GroupModelChangeKind.EDITOR_LABEL) { label1ChangeCounter++; } - }); + })); let label2ChangeCounter = 0; - group2.onDidModelChange((e) => { + disposables.add(group2.onDidModelChange((e) => { if (e.kind === GroupModelChangeKind.EDITOR_LABEL) { label2ChangeCounter++; } - }); + })); (input1).setDirty(); (input1).setLabel(); @@ -2380,4 +2389,6 @@ suite('EditorGroupModel', () => { assert.strictEqual(group2Events.unsticky[0].editor, input1group2); assert.strictEqual(group2Events.unsticky[0].editorIndex, 1); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts index 3fe93c47cf6..4c1b74c31e7 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorInput.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IResourceEditorInput, ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { DEFAULT_EDITOR_ASSOCIATION, IResourceDiffEditorInput, IResourceMergeEditorInput, IResourceSideBySideEditorInput, isEditorInput, isResourceDiffEditorInput, isResourceEditorInput, isResourceMergeEditorInput, isResourceSideBySideEditorInput, isUntitledResourceEditorInput, IUntitledTextResourceEditorInput } from 'vs/workbench/common/editor'; @@ -85,8 +86,8 @@ suite('EditorInput', () => { test('basics', () => { let counter = 0; - const input = new MyEditorInput(); - const otherInput = new MyEditorInput(); + const input = disposables.add(new MyEditorInput()); + const otherInput = disposables.add(new MyEditorInput()); assert.ok(isEditorInput(input)); assert.ok(!isEditorInput(undefined)); @@ -103,10 +104,10 @@ suite('EditorInput', () => { assert(!input.matches(otherInput)); assert(input.getName()); - input.onWillDispose(() => { + disposables.add(input.onWillDispose(() => { assert(true); counter++; - }); + })); input.dispose(); assert.strictEqual(counter, 1); @@ -115,7 +116,7 @@ suite('EditorInput', () => { test('untyped matches', () => { const testInputID = 'untypedMatches'; const testInputResource = URI.file('/fake'); - const testInput = new TestEditorInput(testInputResource, testInputID); + const testInput = disposables.add(new TestEditorInput(testInputResource, testInputID)); const testUntypedInput = { resource: testInputResource, options: { override: testInputID } }; const tetUntypedInputWrongResource = { resource: URI.file('/incorrectFake'), options: { override: testInputID } }; const testUntypedInputWrongId = { resource: testInputResource, options: { override: 'wrongId' } }; @@ -125,7 +126,6 @@ suite('EditorInput', () => { assert.ok(!testInput.matches(tetUntypedInputWrongResource)); assert.ok(!testInput.matches(testUntypedInputWrongId)); assert.ok(!testInput.matches(testUntypedInputWrong)); - }); test('Untpyed inputs properly match TextResourceEditorInput', () => { @@ -235,4 +235,6 @@ suite('EditorInput', () => { fileEditorInput1.dispose(); fileEditorInput2.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts index 2548c2db93e..76c2ca3d373 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorModel.test.ts @@ -35,6 +35,8 @@ import { ILanguageConfigurationService } from 'vs/editor/common/languages/langua import { TestAccessibilityService } from 'vs/platform/accessibility/test/common/testAccessibilityService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('EditorModel', () => { @@ -61,33 +63,35 @@ suite('EditorModel', () => { instantiationService.stub(IUndoRedoService, undoRedoService); instantiationService.stub(IEditorService, new TestEditorService()); instantiationService.stub(IThemeService, new TestThemeService()); - instantiationService.stub(ILanguageConfigurationService, new TestLanguageConfigurationService()); - instantiationService.stub(IStorageService, new TestStorageService()); + instantiationService.stub(ILanguageConfigurationService, disposables.add(new TestLanguageConfigurationService())); + instantiationService.stub(IStorageService, disposables.add(new TestStorageService())); - return instantiationService.createInstance(ModelService); + return disposables.add(instantiationService.createInstance(ModelService)); } let instantiationService: TestInstantiationService; let languageService: ILanguageService; + const disposables = new DisposableStore(); + setup(() => { - instantiationService = new TestInstantiationService(); + instantiationService = disposables.add(new TestInstantiationService()); languageService = instantiationService.stub(ILanguageService, LanguageService); }); teardown(() => { - instantiationService.dispose(); + disposables.clear(); }); test('basics', async () => { let counter = 0; - const model = new MyEditorModel(); + const model = disposables.add(new MyEditorModel()); - model.onWillDispose(() => { + disposables.add(model.onWillDispose(() => { assert(true); counter++; - }); + })); await model.resolve(); assert.strictEqual(model.isDisposed(), false); @@ -100,11 +104,12 @@ suite('EditorModel', () => { test('BaseTextEditorModel', async () => { const modelService = stubModelService(instantiationService); - const model = new MyTextEditorModel(modelService, languageService, instantiationService.createInstance(LanguageDetectionService), instantiationService.createInstance(TestAccessibilityService)); + const model = disposables.add(new MyTextEditorModel(modelService, languageService, disposables.add(instantiationService.createInstance(LanguageDetectionService)), instantiationService.createInstance(TestAccessibilityService))); await model.resolve(); - model.testCreateTextEditorModel(createTextBufferFactory('foo'), null!, Mimes.text); + disposables.add(model.testCreateTextEditorModel(createTextBufferFactory('foo'), null!, Mimes.text)); assert.strictEqual(model.isResolved(), true); - model.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts index d1a44ee58f6..f44f395fac6 100644 --- a/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/resourceEditorInput.test.ts @@ -13,6 +13,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { EditorInputCapabilities, Verbosity } from 'vs/workbench/common/editor'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('ResourceEditorInput', () => { @@ -44,7 +45,7 @@ suite('ResourceEditorInput', () => { test('basics', async () => { const resource = URI.from({ scheme: 'testResource', path: 'thePath/of/the/resource.txt' }); - const input = instantiationService.createInstance(TestResourceEditorInput, resource); + const input = disposables.add(instantiationService.createInstance(TestResourceEditorInput, resource)); assert.ok(input.getName().length > 0); @@ -60,4 +61,6 @@ suite('ResourceEditorInput', () => { assert.strictEqual(input.isReadonly(), false); assert.strictEqual(input.hasCapability(EditorInputCapabilities.Untitled), true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts index 7eeb6e81a60..cbd6264885f 100644 --- a/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/sideBySideEditorInput.test.ts @@ -6,6 +6,7 @@ import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { EditorResourceAccessor, IResourceSideBySideEditorInput, isResourceSideBySideEditorInput, isSideBySideEditorInput, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; @@ -58,19 +59,19 @@ suite('SideBySideEditorInput', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); let counter = 0; - const input = new MyEditorInput(URI.file('/fake')); - input.onWillDispose(() => { + const input = disposables.add(new MyEditorInput(URI.file('/fake'))); + disposables.add(input.onWillDispose(() => { assert(true); counter++; - }); + })); - const otherInput = new MyEditorInput(URI.file('/fake2')); - otherInput.onWillDispose(() => { + const otherInput = disposables.add(new MyEditorInput(URI.file('/fake2'))); + disposables.add(otherInput.onWillDispose(() => { assert(true); counter++; - }); + })); - const sideBySideInput = instantiationService.createInstance(SideBySideEditorInput, 'name', 'description', input, otherInput); + const sideBySideInput = disposables.add(instantiationService.createInstance(SideBySideEditorInput, 'name', 'description', input, otherInput)); assert.strictEqual(sideBySideInput.getName(), 'name'); assert.strictEqual(sideBySideInput.getDescription(), 'description'); @@ -85,7 +86,7 @@ suite('SideBySideEditorInput', () => { sideBySideInput.dispose(); assert.strictEqual(counter, 0); - const sideBySideInputSame = instantiationService.createInstance(SideBySideEditorInput, undefined, undefined, input, input); + const sideBySideInputSame = disposables.add(instantiationService.createInstance(SideBySideEditorInput, undefined, undefined, input, input)); assert.strictEqual(sideBySideInputSame.getName(), input.getName()); assert.strictEqual(sideBySideInputSame.getDescription(), input.getDescription()); assert.strictEqual(sideBySideInputSame.getTitle(), input.getTitle()); @@ -95,21 +96,21 @@ suite('SideBySideEditorInput', () => { test('events dispatching', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); - const input = new MyEditorInput(); - const otherInput = new MyEditorInput(); + const input = disposables.add(new MyEditorInput()); + const otherInput = disposables.add(new MyEditorInput()); - const sideBySideInut = instantiationService.createInstance(SideBySideEditorInput, 'name', 'description', otherInput, input); + const sideBySideInut = disposables.add(instantiationService.createInstance(SideBySideEditorInput, 'name', 'description', otherInput, input)); assert.ok(isSideBySideEditorInput(sideBySideInut)); let capabilitiesChangeCounter = 0; - sideBySideInut.onDidChangeCapabilities(() => capabilitiesChangeCounter++); + disposables.add(sideBySideInut.onDidChangeCapabilities(() => capabilitiesChangeCounter++)); let dirtyChangeCounter = 0; - sideBySideInut.onDidChangeDirty(() => dirtyChangeCounter++); + disposables.add(sideBySideInut.onDidChangeDirty(() => dirtyChangeCounter++)); let labelChangeCounter = 0; - sideBySideInut.onDidChangeLabel(() => labelChangeCounter++); + disposables.add(sideBySideInut.onDidChangeLabel(() => labelChangeCounter++)); input.fireCapabilitiesChangeEvent(); assert.strictEqual(capabilitiesChangeCounter, 1); @@ -129,10 +130,10 @@ suite('SideBySideEditorInput', () => { test('toUntyped', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); - const primaryInput = new MyEditorInput(URI.file('/fake')); - const secondaryInput = new MyEditorInput(URI.file('/fake2')); + const primaryInput = disposables.add(new MyEditorInput(URI.file('/fake'))); + const secondaryInput = disposables.add(new MyEditorInput(URI.file('/fake2'))); - const sideBySideInput = instantiationService.createInstance(SideBySideEditorInput, 'Side By Side Test', undefined, secondaryInput, primaryInput); + const sideBySideInput = disposables.add(instantiationService.createInstance(SideBySideEditorInput, 'Side By Side Test', undefined, secondaryInput, primaryInput)); const untypedSideBySideInput = sideBySideInput.toUntyped(); assert.ok(isResourceSideBySideEditorInput(untypedSideBySideInput)); @@ -141,9 +142,9 @@ suite('SideBySideEditorInput', () => { test('untyped matches', () => { const instantiationService = workbenchInstantiationService(undefined, disposables); - const primaryInput = new TestFileEditorInput(URI.file('/fake'), 'primaryId'); - const secondaryInput = new TestFileEditorInput(URI.file('/fake2'), 'secondaryId'); - const sideBySideInput = instantiationService.createInstance(SideBySideEditorInput, 'Side By Side Test', undefined, secondaryInput, primaryInput); + const primaryInput = disposables.add(new TestFileEditorInput(URI.file('/fake'), 'primaryId')); + const secondaryInput = disposables.add(new TestFileEditorInput(URI.file('/fake2'), 'secondaryId')); + const sideBySideInput = disposables.add(instantiationService.createInstance(SideBySideEditorInput, 'Side By Side Test', undefined, secondaryInput, primaryInput)); const primaryUntypedInput = { resource: URI.file('/fake'), options: { override: 'primaryId' } }; const secondaryUntypedInput = { resource: URI.file('/fake2'), options: { override: 'secondaryId' } }; @@ -163,4 +164,6 @@ suite('SideBySideEditorInput', () => { assert.ok(!sideBySideInput.matches(sideBySideUntyped3)); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts b/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts index 797e6c88c2f..029ad438984 100644 --- a/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/textResourceEditorInput.test.ts @@ -12,6 +12,7 @@ import { workbenchInstantiationService, TestServiceAccessor } from 'vs/workbench import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles'; import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('TextResourceEditorInput', () => { @@ -33,9 +34,9 @@ suite('TextResourceEditorInput', () => { const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.languageService.createById(PLAINTEXT_LANGUAGE_ID), resource); - const input = instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, undefined); + const input = disposables.add(instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, undefined)); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.ok(model); assert.strictEqual(snapshotToString(((model as TextResourceEditorModel).createSnapshot()!)), 'function test() {}'); @@ -49,16 +50,16 @@ suite('TextResourceEditorInput', () => { const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.languageService.createById(PLAINTEXT_LANGUAGE_ID), resource); - const input = instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', 'resource-input-test', undefined); + const input = disposables.add(instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', 'resource-input-test', undefined)); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.ok(model); assert.strictEqual(model.textEditorModel?.getLanguageId(), 'resource-input-test'); input.setLanguageId('text'); assert.strictEqual(model.textEditorModel?.getLanguageId(), PLAINTEXT_LANGUAGE_ID); - await input.resolve(); + disposables.add(await input.resolve()); assert.strictEqual(model.textEditorModel?.getLanguageId(), PLAINTEXT_LANGUAGE_ID); registration.dispose(); }); @@ -71,10 +72,10 @@ suite('TextResourceEditorInput', () => { const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.languageService.createById(PLAINTEXT_LANGUAGE_ID), resource); - const input = instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, undefined); + const input = disposables.add(instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, undefined)); input.setPreferredLanguageId('resource-input-test'); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.ok(model); assert.strictEqual(model.textEditorModel?.getLanguageId(), 'resource-input-test'); registration.dispose(); @@ -84,16 +85,16 @@ suite('TextResourceEditorInput', () => { const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.languageService.createById(PLAINTEXT_LANGUAGE_ID), resource); - const input = instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, 'My Resource Input Contents'); + const input = disposables.add(instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, 'My Resource Input Contents')); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.ok(model); assert.strictEqual(model.textEditorModel?.getValue(), 'My Resource Input Contents'); model.textEditorModel.setValue('Some other contents'); assert.strictEqual(model.textEditorModel?.getValue(), 'Some other contents'); - await input.resolve(); + disposables.add(await input.resolve()); assert.strictEqual(model.textEditorModel?.getValue(), 'Some other contents'); // preferred contents only used once }); @@ -101,17 +102,19 @@ suite('TextResourceEditorInput', () => { const resource = URI.from({ scheme: 'inmemory', authority: null!, path: 'thePath' }); accessor.modelService.createModel('function test() {}', accessor.languageService.createById(PLAINTEXT_LANGUAGE_ID), resource); - const input = instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, undefined); + const input = disposables.add(instantiationService.createInstance(TextResourceEditorInput, resource, 'The Name', 'The Description', undefined, undefined)); input.setPreferredContents('My Resource Input Contents'); - const model = await input.resolve(); + const model = disposables.add(await input.resolve()); assert.ok(model); assert.strictEqual(model.textEditorModel?.getValue(), 'My Resource Input Contents'); model.textEditorModel.setValue('Some other contents'); assert.strictEqual(model.textEditorModel?.getValue(), 'Some other contents'); - await input.resolve(); + disposables.add(await input.resolve()); assert.strictEqual(model.textEditorModel?.getValue(), 'Some other contents'); // preferred contents only used once }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/parts/statusbar/statusbarModel.test.ts b/src/vs/workbench/test/browser/parts/statusbar/statusbarModel.test.ts index c42d46e4d88..3f0f767be42 100644 --- a/src/vs/workbench/test/browser/parts/statusbar/statusbarModel.test.ts +++ b/src/vs/workbench/test/browser/parts/statusbar/statusbarModel.test.ts @@ -7,12 +7,20 @@ import * as assert from 'assert'; import { StatusbarViewModel } from 'vs/workbench/browser/parts/statusbar/statusbarModel'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Workbench status bar model', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('basics', () => { const container = document.createElement('div'); - const model = new StatusbarViewModel(new TestStorageService()); + const model = disposables.add(new StatusbarViewModel(disposables.add(new TestStorageService()))); assert.strictEqual(model.entries.length, 0); @@ -44,9 +52,9 @@ suite('Workbench status bar model', () => { assert.ok(model.findEntry(container)); let didChangeEntryVisibility: { id: string; visible: boolean } = { id: '', visible: false }; - model.onDidChangeEntryVisibility(e => { + disposables.add(model.onDidChangeEntryVisibility(e => { didChangeEntryVisibility = e; - }); + })); assert.strictEqual(model.isHidden('1'), false); model.hide('1'); @@ -72,7 +80,7 @@ suite('Workbench status bar model', () => { test('secondary priority used when primary is same', () => { const container = document.createElement('div'); - const model = new StatusbarViewModel(new TestStorageService()); + const model = disposables.add(new StatusbarViewModel(disposables.add(new TestStorageService()))); assert.strictEqual(model.entries.length, 0); @@ -88,7 +96,7 @@ suite('Workbench status bar model', () => { test('insertion order preserved when priorites are the same', () => { const container = document.createElement('div'); - const model = new StatusbarViewModel(new TestStorageService()); + const model = disposables.add(new StatusbarViewModel(disposables.add(new TestStorageService()))); assert.strictEqual(model.entries.length, 0); @@ -104,7 +112,7 @@ suite('Workbench status bar model', () => { test('entry with reference to other entry (existing)', () => { const container = document.createElement('div'); - const model = new StatusbarViewModel(new TestStorageService()); + const model = disposables.add(new StatusbarViewModel(disposables.add(new TestStorageService()))); // Existing reference, Alignment: left model.add({ id: 'a', alignment: StatusbarAlignment.LEFT, name: '1', priority: { primary: 2, secondary: 1 }, container, labelContainer: container, hasCommand: false }); @@ -134,7 +142,7 @@ suite('Workbench status bar model', () => { test('entry with reference to other entry (nonexistent)', () => { const container = document.createElement('div'); - const model = new StatusbarViewModel(new TestStorageService()); + const model = disposables.add(new StatusbarViewModel(disposables.add(new TestStorageService()))); // Nonexistent reference, Alignment: left model.add({ id: 'a', alignment: StatusbarAlignment.LEFT, name: '1', priority: { primary: 2, secondary: 1 }, container, labelContainer: container, hasCommand: false }); @@ -164,7 +172,7 @@ suite('Workbench status bar model', () => { test('entry with reference to other entry resorts based on other entry being there or not', () => { const container = document.createElement('div'); - const model = new StatusbarViewModel(new TestStorageService()); + const model = disposables.add(new StatusbarViewModel(disposables.add(new TestStorageService()))); model.add({ id: 'a', alignment: StatusbarAlignment.LEFT, name: '1', priority: { primary: 2, secondary: 1 }, container, labelContainer: container, hasCommand: false }); model.add({ id: 'b', alignment: StatusbarAlignment.LEFT, name: '2', priority: { primary: 1, secondary: 1 }, container, labelContainer: container, hasCommand: false }); @@ -197,7 +205,7 @@ suite('Workbench status bar model', () => { test('entry with reference to other entry but different alignment does not explode', () => { const container = document.createElement('div'); - const model = new StatusbarViewModel(new TestStorageService()); + const model = disposables.add(new StatusbarViewModel(disposables.add(new TestStorageService()))); model.add({ id: '1-left', alignment: StatusbarAlignment.LEFT, name: '1-left', priority: { primary: 2, secondary: 1 }, container, labelContainer: container, hasCommand: false }); model.add({ id: '2-left', alignment: StatusbarAlignment.LEFT, name: '2-left', priority: { primary: 1, secondary: 1 }, container, labelContainer: container, hasCommand: false }); @@ -223,4 +231,6 @@ suite('Workbench status bar model', () => { assert.strictEqual(model.getEntries(StatusbarAlignment.LEFT).length, 2); assert.strictEqual(model.getEntries(StatusbarAlignment.RIGHT).length, 3); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/browser/viewlet.test.ts b/src/vs/workbench/test/browser/viewlet.test.ts index da133da18e2..eefc87cb2d6 100644 --- a/src/vs/workbench/test/browser/viewlet.test.ts +++ b/src/vs/workbench/test/browser/viewlet.test.ts @@ -8,6 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { PaneCompositeDescriptor, Extensions, PaneCompositeRegistry, PaneComposite } from 'vs/workbench/browser/panecomposite'; import { isFunction } from 'vs/base/common/types'; import { IBoundarySashes } from 'vs/base/browser/ui/sash/sash'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Viewlets', () => { @@ -58,4 +59,6 @@ suite('Viewlets', () => { assert(d === Registry.as(Extensions.Viewlets).getPaneComposite('reg-test-id')); assert.strictEqual(oldCount + 1, Registry.as(Extensions.Viewlets).getPaneComposites().length); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/common/memento.test.ts b/src/vs/workbench/test/common/memento.test.ts index e523402f4bd..84502b1c321 100644 --- a/src/vs/workbench/test/common/memento.test.ts +++ b/src/vs/workbench/test/common/memento.test.ts @@ -4,20 +4,27 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage'; import { Memento } from 'vs/workbench/common/memento'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; suite('Memento', () => { + const disposables = new DisposableStore(); let storage: IStorageService; setup(() => { - storage = new TestStorageService(); + storage = disposables.add(new TestStorageService()); Memento.clear(StorageScope.APPLICATION); Memento.clear(StorageScope.PROFILE); Memento.clear(StorageScope.WORKSPACE); }); + teardown(() => { + disposables.clear(); + }); + test('Loading and Saving Memento with Scopes', () => { const myMemento = new Memento('memento.test', storage); @@ -213,7 +220,7 @@ suite('Memento', () => { myMemento.saveMemento(); // Clear - storage = new TestStorageService(); + storage = disposables.add(new TestStorageService()); Memento.clear(StorageScope.PROFILE); Memento.clear(StorageScope.WORKSPACE); @@ -224,4 +231,6 @@ suite('Memento', () => { assert.deepStrictEqual(profileMemento, {}); assert.deepStrictEqual(workspaceMemento, {}); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/common/notifications.test.ts b/src/vs/workbench/test/common/notifications.test.ts index bc96b826ea9..651d70c43cc 100644 --- a/src/vs/workbench/test/common/notifications.test.ts +++ b/src/vs/workbench/test/common/notifications.test.ts @@ -11,9 +11,17 @@ import { createErrorWithActions } from 'vs/base/common/errorMessage'; import { NotificationService } from 'vs/workbench/services/notification/common/notificationService'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { timeout } from 'vs/base/common/async'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('Notifications', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('Items', () => { // Invalid @@ -25,8 +33,8 @@ suite('Notifications', () => { const item2 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' })!; const item3 = NotificationViewItem.create({ severity: Severity.Info, message: 'Info Message' })!; const item4 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', source: 'Source' })!; - const item5 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] } })!; - const item6 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] }, progress: { infinite: true } })!; + const item5 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [disposables.add(new Action('id', 'label'))] } })!; + const item6 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message', actions: { primary: [disposables.add(new Action('id', 'label'))] }, progress: { infinite: true } })!; assert.strictEqual(item1.equals(item1), true); assert.strictEqual(item2.equals(item2), true); @@ -55,9 +63,9 @@ suite('Notifications', () => { // Events let called = 0; - item1.onDidChangeExpansion(() => { + disposables.add(item1.onDidChangeExpansion(() => { called++; - }); + })); item1.expand(); item1.expand(); @@ -67,11 +75,11 @@ suite('Notifications', () => { assert.strictEqual(called, 2); called = 0; - item1.onDidChangeContent(e => { + disposables.add(item1.onDidChangeContent(e => { if (e.kind === NotificationViewItemContentChangeKind.PROGRESS) { called++; } - }); + })); item1.progress.infinite(); item1.progress.done(); @@ -79,38 +87,38 @@ suite('Notifications', () => { assert.strictEqual(called, 2); called = 0; - item1.onDidChangeContent(e => { + disposables.add(item1.onDidChangeContent(e => { if (e.kind === NotificationViewItemContentChangeKind.MESSAGE) { called++; } - }); + })); item1.updateMessage('message update'); called = 0; - item1.onDidChangeContent(e => { + disposables.add(item1.onDidChangeContent(e => { if (e.kind === NotificationViewItemContentChangeKind.SEVERITY) { called++; } - }); + })); item1.updateSeverity(Severity.Error); called = 0; - item1.onDidChangeContent(e => { + disposables.add(item1.onDidChangeContent(e => { if (e.kind === NotificationViewItemContentChangeKind.ACTIONS) { called++; } - }); + })); - item1.updateActions({ primary: [new Action('id2', 'label')] }); + item1.updateActions({ primary: [disposables.add(new Action('id2', 'label'))] }); assert.strictEqual(called, 1); called = 0; - item1.onDidChangeVisibility(e => { + disposables.add(item1.onDidChangeVisibility(e => { called++; - }); + })); item1.updateVisibility(true); item1.updateVisibility(false); @@ -119,15 +127,15 @@ suite('Notifications', () => { assert.strictEqual(called, 2); called = 0; - item1.onDidClose(() => { + disposables.add(item1.onDidClose(() => { called++; - }); + })); item1.close(); assert.strictEqual(called, 1); // Error with Action - const item7 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', [new Action('id', 'label')]) })!; + const item7 = NotificationViewItem.create({ severity: Severity.Error, message: createErrorWithActions('Hello Error', [disposables.add(new Action('id', 'label'))]) })!; assert.strictEqual(item7.actions!.primary!.length, 1); // Filter @@ -142,15 +150,19 @@ suite('Notifications', () => { const item11 = NotificationViewItem.create({ severity: Severity.Warning, message: 'Error Message' }, NotificationsFilter.ERROR)!; assert.strictEqual(item11.priority, NotificationPriority.SILENT); + + for (const item of [item1, item2, item3, item4, item5, item6, itemId1, itemId2, item7, item8, item9, item10, item11]) { + item.close(); + } }); test('Items - does not fire changed when message did not change (content, severity)', async () => { const item1 = NotificationViewItem.create({ severity: Severity.Error, message: 'Error Message' })!; let fired = false; - item1.onDidChangeContent(() => { + disposables.add(item1.onDidChangeContent(() => { fired = true; - }); + })); item1.updateMessage('Error Message'); await timeout(0); @@ -159,22 +171,26 @@ suite('Notifications', () => { item1.updateSeverity(Severity.Error); await timeout(0); assert.ok(!fired, 'Expected onDidChangeContent to not be fired'); + + for (const item of [item1]) { + item.close(); + } }); test('Model', () => { - const model = new NotificationsModel(); + const model = disposables.add(new NotificationsModel()); let lastNotificationEvent!: INotificationChangeEvent; - model.onDidChangeNotification(e => { + disposables.add(model.onDidChangeNotification(e => { lastNotificationEvent = e; - }); + })); let lastStatusMessageEvent!: IStatusMessageChangeEvent; - model.onDidChangeStatusMessage(e => { + disposables.add(model.onDidChangeStatusMessage(e => { lastStatusMessageEvent = e; - }); + })); - const item1: INotification = { severity: Severity.Error, message: 'Error Message', actions: { primary: [new Action('id', 'label')] } }; + const item1: INotification = { severity: Severity.Error, message: 'Error Message', actions: { primary: [disposables.add(new Action('id', 'label'))] } }; const item2: INotification = { severity: Severity.Warning, message: 'Warning Message', source: 'Some Source' }; const item2Duplicate: INotification = { severity: Severity.Warning, message: 'Warning Message', source: 'Some Source' }; const item3: INotification = { severity: Severity.Info, message: 'Info Message' }; @@ -207,7 +223,7 @@ suite('Notifications', () => { assert.strictEqual(lastNotificationEvent.index, 0); assert.strictEqual(lastNotificationEvent.kind, NotificationChangeType.ADD); - model.addNotification(item3); + const item3Handle = model.addNotification(item3); assert.strictEqual(lastNotificationEvent.item.severity, item3.severity); assert.strictEqual(lastNotificationEvent.item.message.linkedText.toString(), item3.message); assert.strictEqual(lastNotificationEvent.index, 0); @@ -216,9 +232,9 @@ suite('Notifications', () => { assert.strictEqual(model.notifications.length, 3); let called = 0; - item1Handle.onDidClose(() => { + disposables.add(item1Handle.onDidClose(() => { called++; - }); + })); item1Handle.close(); assert.strictEqual(called, 1); @@ -228,7 +244,7 @@ suite('Notifications', () => { assert.strictEqual(lastNotificationEvent.index, 2); assert.strictEqual(lastNotificationEvent.kind, NotificationChangeType.REMOVE); - model.addNotification(item2Duplicate); + const item2DuplicateHandle = model.addNotification(item2Duplicate); assert.strictEqual(model.notifications.length, 2); assert.strictEqual(lastNotificationEvent.item.severity, item2Duplicate.severity); assert.strictEqual(lastNotificationEvent.item.message.linkedText.toString(), item2Duplicate.message); @@ -266,22 +282,26 @@ suite('Notifications', () => { disposable3.dispose(); assert.ok(!model.statusMessage); + + item2DuplicateHandle.close(); + item3Handle.close(); }); test('Service', async () => { - const service = new NotificationService(new TestStorageService()); + const service = disposables.add(new NotificationService(disposables.add(new TestStorageService()))); let addNotificationCount = 0; let notification!: INotification; - service.onDidAddNotification(n => { + disposables.add(service.onDidAddNotification(n => { addNotificationCount++; notification = n; - }); + })); service.info('hello there'); assert.strictEqual(addNotificationCount, 1); assert.strictEqual(notification.message, 'hello there'); assert.strictEqual(notification.priority, NotificationPriority.DEFAULT); assert.strictEqual(notification.source, undefined); + service.model.notifications[0].close(); let notificationHandle = service.notify({ message: 'important message', severity: Severity.Warning }); assert.strictEqual(addNotificationCount, 2); @@ -289,10 +309,10 @@ suite('Notifications', () => { assert.strictEqual(notification.severity, Severity.Warning); let removeNotificationCount = 0; - service.onDidRemoveNotification(n => { + disposables.add(service.onDidRemoveNotification(n => { removeNotificationCount++; notification = n; - }); + })); notificationHandle.close(); assert.strictEqual(removeNotificationCount, 1); assert.strictEqual(notification.message, 'important message'); @@ -303,5 +323,8 @@ suite('Notifications', () => { assert.strictEqual(notification.priority, NotificationPriority.SILENT); notificationHandle.close(); assert.strictEqual(removeNotificationCount, 2); + notificationHandle.close(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/test/common/resources.test.ts b/src/vs/workbench/test/common/resources.test.ts index a7849b94648..6ce08bcf9ea 100644 --- a/src/vs/workbench/test/common/resources.test.ts +++ b/src/vs/workbench/test/common/resources.test.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ResourceGlobMatcher } from 'vs/workbench/common/resources'; @@ -17,6 +19,8 @@ suite('ResourceGlobMatcher', () => { let contextService: IWorkspaceContextService; let configurationService: TestConfigurationService; + const disposables = new DisposableStore(); + setup(() => { contextService = new TestContextService(); configurationService = new TestConfigurationService({ @@ -27,8 +31,12 @@ suite('ResourceGlobMatcher', () => { }); }); + teardown(() => { + disposables.clear(); + }); + test('Basics', async () => { - const matcher = new ResourceGlobMatcher(() => configurationService.getValue(SETTING), e => e.affectsConfiguration(SETTING), contextService, configurationService); + const matcher = disposables.add(new ResourceGlobMatcher(() => configurationService.getValue(SETTING), e => e.affectsConfiguration(SETTING), contextService, configurationService)); // Matching assert.equal(matcher.matches(URI.file('/foo/bar')), false); @@ -37,7 +45,7 @@ suite('ResourceGlobMatcher', () => { // Events let eventCounter = 0; - matcher.onExpressionChange(() => eventCounter++); + disposables.add(matcher.onExpressionChange(() => eventCounter++)); await configurationService.setUserConfiguration(SETTING, { '**/*.foo': true }); configurationService.onDidChangeConfigurationEmitter.fire({ affectsConfiguration: (key: string) => key === SETTING } as any); @@ -64,4 +72,6 @@ suite('ResourceGlobMatcher', () => { assert.equal(matcher.matches(URI.file('/bar/foo.1')), true); assert.equal(matcher.matches(URI.file('C:/bar/foo.1')), true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); From 971db807fb51215e5e7de48dced8b8e8e352dfa1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 7 Sep 2023 16:13:30 +0200 Subject: [PATCH 602/607] Fix tab index of tree checkbox (#192413) Fixes #189342 --- src/vs/workbench/browser/parts/views/checkbox.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/views/checkbox.ts b/src/vs/workbench/browser/parts/views/checkbox.ts index f5c3b4241a5..849966bbe33 100644 --- a/src/vs/workbench/browser/parts/views/checkbox.ts +++ b/src/vs/workbench/browser/parts/views/checkbox.ts @@ -62,6 +62,7 @@ export class TreeItemCheckbox extends Disposable { this.setHover(node.checkbox); this.setAccessibilityInformation(node.checkbox); this.toggle.domNode.classList.add(TreeItemCheckbox.checkboxClass); + this.toggle.domNode.tabIndex = 1; DOM.append(this.checkboxContainer, this.toggle.domNode); this.registerListener(node); } From c831e73d5fbba72cd46914d83cbf612dfa8dba6c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 7 Sep 2023 16:51:32 +0200 Subject: [PATCH 603/607] debt - ensure more dispose from tests (#192415) --- src/vs/base/common/async.ts | 5 ++ src/vs/base/common/lifecycle.ts | 4 + .../test/browser/highlightedLabel.test.ts | 4 +- src/vs/base/test/browser/progressBar.test.ts | 3 + src/vs/base/test/common/extpath.test.ts | 3 + src/vs/base/test/common/fuzzyScorer.test.ts | 3 + src/vs/base/test/common/glob.test.ts | 3 + src/vs/base/test/common/labels.test.ts | 3 + src/vs/base/test/common/stream.test.ts | 3 + src/vs/base/test/node/extpath.test.ts | 3 + src/vs/base/test/node/pfs/pfs.test.ts | 3 + .../electron-main/backupMainService.test.ts | 3 + .../environmentMainService.test.ts | 3 + .../test/node/environmentService.test.ts | 3 + .../files/test/browser/fileService.test.ts | 51 ++++++------ .../platform/files/test/common/files.test.ts | 4 +- .../files/test/common/watcher.test.ts | 60 ++++++++------ .../test/electron-main/windowsFinder.test.ts | 3 + .../electron-main/windowsStateHandler.test.ts | 3 + .../workspacesHistoryStorage.test.ts | 3 + .../workspacesManagementMainService.test.ts | 3 + .../test/browser/outputLinkProvider.test.ts | 3 + .../services/label/test/browser/label.test.ts | 27 +++++-- .../test/browser/progressIndicator.test.ts | 16 +++- .../test/browser/textFileEditorModel.test.ts | 80 +++++++++---------- .../test/node/encoding/encoding.test.ts | 3 + .../common/storedFileWorkingCopy.ts | 4 +- .../test/browser/resourceWorkingCopy.test.ts | 8 +- .../browser/storedFileWorkingCopy.test.ts | 22 +++-- .../storedFileWorkingCopyManager.test.ts | 60 +++++++------- .../browser/untitledFileWorkingCopy.test.ts | 35 ++++---- .../untitledScratchpadWorkingCopy.test.ts | 35 ++++---- .../test/common/workingCopyService.test.ts | 44 ++++++---- 33 files changed, 315 insertions(+), 195 deletions(-) diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index 95d17f691d8..dad5135fd56 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -754,6 +754,11 @@ export class ResourceQueue implements IDisposable { this.onDidQueueDrain(); this.drainListeners?.deleteAndDispose(drainListenerId); + + if (this.drainListeners?.size === 0) { + this.drainListeners.dispose(); + this.drainListeners = undefined; + } }); if (!this.drainListeners) { diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 1682b7932ae..3d5dd09598d 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -539,6 +539,10 @@ export class DisposableMap implements ID return this._store.has(key); } + get size(): number { + return this._store.size; + } + get(key: K): V | undefined { return this._store.get(key); } diff --git a/src/vs/base/test/browser/highlightedLabel.test.ts b/src/vs/base/test/browser/highlightedLabel.test.ts index 1bff745515b..4f5eb5ca015 100644 --- a/src/vs/base/test/browser/highlightedLabel.test.ts +++ b/src/vs/base/test/browser/highlightedLabel.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('HighlightedLabel', () => { let label: HighlightedLabel; @@ -58,6 +59,7 @@ suite('HighlightedLabel', () => { escaped = HighlightedLabel.escapeNewLines('ACTION\r\n_TYPE2', highlights); assert.strictEqual(escaped, 'ACTION\u23CE_TYPE2'); assert.deepStrictEqual(highlights, [{ start: 5, end: 8 }, { start: 10, end: 11 }]); - }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/browser/progressBar.test.ts b/src/vs/base/test/browser/progressBar.test.ts index f43082a0bc6..eb9790cd6ce 100644 --- a/src/vs/base/test/browser/progressBar.test.ts +++ b/src/vs/base/test/browser/progressBar.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('ProgressBar', () => { let fixture: HTMLElement; @@ -29,4 +30,6 @@ suite('ProgressBar', () => { bar.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/common/extpath.test.ts b/src/vs/base/test/common/extpath.test.ts index 1c37ca687b6..3c6a4e4979b 100644 --- a/src/vs/base/test/common/extpath.test.ts +++ b/src/vs/base/test/common/extpath.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { CharCode } from 'vs/base/common/charCode'; import * as extpath from 'vs/base/common/extpath'; import { isWindows } from 'vs/base/common/platform'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Paths', () => { @@ -219,4 +220,6 @@ suite('Paths', () => { const r4 = extpath.randomPath(); assert.ok(r4); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/common/fuzzyScorer.test.ts b/src/vs/base/test/common/fuzzyScorer.test.ts index ac3f2aab30f..03417dea597 100644 --- a/src/vs/base/test/common/fuzzyScorer.test.ts +++ b/src/vs/base/test/common/fuzzyScorer.test.ts @@ -9,6 +9,7 @@ import { Schemas } from 'vs/base/common/network'; import { basename, dirname, posix, sep, win32 } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class ResourceAccessorClass implements IItemAccessor { @@ -1250,4 +1251,6 @@ suite('Fuzzy Scorer', () => { assert.strictEqual(score[1][0], 7); assert.strictEqual(score[1][1], 8); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/common/glob.test.ts b/src/vs/base/test/common/glob.test.ts index bbabd7faf91..5bfb3dccfb3 100644 --- a/src/vs/base/test/common/glob.test.ts +++ b/src/vs/base/test/common/glob.test.ts @@ -8,6 +8,7 @@ import * as glob from 'vs/base/common/glob'; import { sep } from 'vs/base/common/path'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Glob', () => { @@ -1156,4 +1157,6 @@ suite('Glob', () => { assert.ok(!glob.patternsEquals(undefined, ['b'])); assert.ok(!glob.patternsEquals(['a'], undefined)); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/common/labels.test.ts b/src/vs/base/test/common/labels.test.ts index f02cd55c523..8928b09c72d 100644 --- a/src/vs/base/test/common/labels.test.ts +++ b/src/vs/base/test/common/labels.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import * as labels from 'vs/base/common/labels'; import { isMacintosh, isWindows, OperatingSystem } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Labels', () => { (!isWindows ? test.skip : test)('shorten - windows', () => { @@ -231,4 +232,6 @@ suite('Labels', () => { assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Macintosh, relative: nixRelativePathProvider }), 'folder/file.txt'); assert.strictEqual(labels.getPathLabel(nixUntitledUri, { os: OperatingSystem.Linux, relative: nixRelativePathProvider }), 'folder/file.txt'); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/common/stream.test.ts b/src/vs/base/test/common/stream.test.ts index 63d30530a95..5589dfdceaf 100644 --- a/src/vs/base/test/common/stream.test.ts +++ b/src/vs/base/test/common/stream.test.ts @@ -8,6 +8,7 @@ import { timeout } from 'vs/base/common/async'; import { bufferToReadable, VSBuffer } from 'vs/base/common/buffer'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { consumeReadable, consumeStream, isReadable, isReadableBufferedStream, isReadableStream, listenStream, newWriteableStream, peekReadable, peekStream, prefixedReadable, prefixedStream, Readable, ReadableStream, toReadable, toStream, transform } from 'vs/base/common/stream'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('Stream', () => { @@ -514,4 +515,6 @@ suite('Stream', () => { } assert.ok(error); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/node/extpath.test.ts b/src/vs/base/test/node/extpath.test.ts index c4e2fd831fb..04aa1873295 100644 --- a/src/vs/base/test/node/extpath.test.ts +++ b/src/vs/base/test/node/extpath.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { tmpdir } from 'os'; import { realcase, realcaseSync, realpath, realpathSync } from 'vs/base/node/extpath'; import { Promises } from 'vs/base/node/pfs'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; flakySuite('Extpath', () => { @@ -79,4 +80,6 @@ flakySuite('Extpath', () => { const realpath = realpathSync(testDir); assert.ok(realpath); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index 3a0fc605a71..b3ef62f232a 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -13,6 +13,7 @@ import { FileAccess } from 'vs/base/common/network'; import { basename, dirname, join, sep } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { configureFlushOnWrite, Promises, RimRafMode, rimrafSync, SymlinkSupport, writeFileSync } from 'vs/base/node/pfs'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; configureFlushOnWrite(false); // speed up all unit tests by disabling flush on write @@ -488,4 +489,6 @@ flakySuite('PFS', function () { writeFileSync(testFile, largeString); assert.strictEqual(fs.readFileSync(testFile).toString(), largeString); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 928d90f7a40..8f3048c267a 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -26,6 +26,7 @@ import { IFolderBackupInfo, isFolderBackupInfo, IWorkspaceBackupInfo } from 'vs/ import { IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { InMemoryTestStateMainService } from 'vs/platform/test/electron-main/workbenchTestServices'; import { LogService } from 'vs/platform/log/common/logService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; flakySuite('BackupMainService', () => { @@ -613,4 +614,6 @@ flakySuite('BackupMainService', () => { assert.strictEqual(found, 2); }); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/environment/test/electron-main/environmentMainService.test.ts b/src/vs/platform/environment/test/electron-main/environmentMainService.test.ts index deb22c605fc..78fd7354520 100644 --- a/src/vs/platform/environment/test/electron-main/environmentMainService.test.ts +++ b/src/vs/platform/environment/test/electron-main/environmentMainService.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { EnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import product from 'vs/platform/product/common/product'; import { isLinux } from 'vs/base/common/platform'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('EnvironmentMainService', () => { @@ -125,4 +126,6 @@ suite('EnvironmentMainService', () => { assert.strictEqual(process.env['TEST_ARG3'], 'test_arg3_non_empty'); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/environment/test/node/environmentService.test.ts b/src/vs/platform/environment/test/node/environmentService.test.ts index 85cb855f1c9..ffe418fc702 100644 --- a/src/vs/platform/environment/test/node/environmentService.test.ts +++ b/src/vs/platform/environment/test/node/environmentService.test.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { parseExtensionHostDebugPort } from 'vs/platform/environment/common/environmentService'; import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; @@ -67,4 +68,6 @@ suite('EnvironmentService', () => { const service2 = new NativeEnvironmentService(args, { _serviceBrand: undefined, ...product }); assert.notStrictEqual(service1.userDataPath, service2.userDataPath); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/files/test/browser/fileService.test.ts b/src/vs/platform/files/test/browser/fileService.test.ts index 79b44bdb46a..5b32db14d64 100644 --- a/src/vs/platform/files/test/browser/fileService.test.ts +++ b/src/vs/platform/files/test/browser/fileService.test.ts @@ -6,9 +6,10 @@ import * as assert from 'assert'; import { DeferredPromise, timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { consumeStream, newWriteableStream, ReadableStreamEvents } from 'vs/base/common/stream'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IFileOpenOptions, IFileReadStreamOptions, FileSystemProviderCapabilities, FileType, IFileSystemProviderCapabilitiesChangeEvent, IFileSystemProviderRegistrationEvent, IStat } from 'vs/platform/files/common/files'; import { FileService } from 'vs/platform/files/common/fileService'; import { NullFileSystemProvider } from 'vs/platform/files/test/common/nullFileSystemProvider'; @@ -16,8 +17,14 @@ import { NullLogService } from 'vs/platform/log/common/log'; suite('File Service', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('provider registration', async () => { - const service = new FileService(new NullLogService()); + const service = disposables.add(new FileService(new NullLogService())); const resource = URI.parse('test://foo/bar'); const provider = new NullFileSystemProvider(); @@ -26,18 +33,18 @@ suite('File Service', () => { assert.strictEqual(service.getProvider(resource.scheme), undefined); const registrations: IFileSystemProviderRegistrationEvent[] = []; - service.onDidChangeFileSystemProviderRegistrations(e => { + disposables.add(service.onDidChangeFileSystemProviderRegistrations(e => { registrations.push(e); - }); + })); const capabilityChanges: IFileSystemProviderCapabilitiesChangeEvent[] = []; - service.onDidChangeFileSystemProviderCapabilities(e => { + disposables.add(service.onDidChangeFileSystemProviderCapabilities(e => { capabilityChanges.push(e); - }); + })); let registrationDisposable: IDisposable | undefined; let callCount = 0; - service.onWillActivateFileSystemProvider(e => { + disposables.add(service.onWillActivateFileSystemProvider(e => { callCount++; if (e.scheme === 'test' && callCount === 1) { @@ -47,7 +54,7 @@ suite('File Service', () => { resolve(); })); } - }); + })); assert.strictEqual(await service.canHandleResource(resource), true); assert.strictEqual(service.hasProvider(resource), true); @@ -79,19 +86,17 @@ suite('File Service', () => { assert.strictEqual(registrations.length, 2); assert.strictEqual(registrations[1].scheme, 'test'); assert.strictEqual(registrations[1].added, false); - - service.dispose(); }); test('watch', async () => { - const service = new FileService(new NullLogService()); + const service = disposables.add(new FileService(new NullLogService())); let disposeCounter = 0; - service.registerProvider('test', new NullFileSystemProvider(() => { + disposables.add(service.registerProvider('test', new NullFileSystemProvider(() => { return toDisposable(() => { disposeCounter++; }); - })); + }))); await service.activateProvider('test'); const resource1 = URI.parse('test://foo/bar1'); @@ -144,7 +149,7 @@ suite('File Service', () => { }); async function testReadErrorBubbles(async: boolean) { - const service = new FileService(new NullLogService()); + const service = disposables.add(new FileService(new NullLogService())); const provider = new class extends NullFileSystemProvider { override async stat(resource: URI): Promise { @@ -158,7 +163,7 @@ suite('File Service', () => { override readFile(resource: URI): Promise { if (async) { - return timeout(5).then(() => { throw new Error('failed'); }); + return timeout(5, CancellationToken.None).then(() => { throw new Error('failed'); }); } throw new Error('failed'); @@ -166,7 +171,7 @@ suite('File Service', () => { override open(resource: URI, opts: IFileOpenOptions): Promise { if (async) { - return timeout(5).then(() => { throw new Error('failed'); }); + return timeout(5, CancellationToken.None).then(() => { throw new Error('failed'); }); } throw new Error('failed'); @@ -175,7 +180,7 @@ suite('File Service', () => { readFileStream(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents { if (async) { const stream = newWriteableStream(chunk => chunk[0]); - timeout(5).then(() => stream.error(new Error('failed'))); + timeout(5, CancellationToken.None).then(() => stream.error(new Error('failed'))); return stream; @@ -185,7 +190,7 @@ suite('File Service', () => { } }; - const disposable = service.registerProvider('test', provider); + disposables.add(service.registerProvider('test', provider)); for (const capabilities of [FileSystemProviderCapabilities.FileReadWrite, FileSystemProviderCapabilities.FileReadStream, FileSystemProviderCapabilities.FileOpenReadWriteClose]) { provider.setCapabilities(capabilities); @@ -209,12 +214,10 @@ suite('File Service', () => { assert.ok(e2); } - - disposable.dispose(); } test('readFile/readFileStream supports cancellation (https://github.com/microsoft/vscode/issues/138805)', async () => { - const service = new FileService(new NullLogService()); + const service = disposables.add(new FileService(new NullLogService())); let readFileStreamReady: DeferredPromise | undefined = undefined; @@ -231,10 +234,10 @@ suite('File Service', () => { readFileStream(resource: URI, opts: IFileReadStreamOptions, token: CancellationToken): ReadableStreamEvents { const stream = newWriteableStream(chunk => chunk[0]); - token.onCancellationRequested(() => { + disposables.add(token.onCancellationRequested(() => { stream.error(new Error('Expected cancellation')); stream.end(); - }); + })); readFileStreamReady!.complete(); @@ -272,4 +275,6 @@ suite('File Service', () => { disposable.dispose(); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/files/test/common/files.test.ts b/src/vs/platform/files/test/common/files.test.ts index 8927ad945b9..2a6ae1a5a3a 100644 --- a/src/vs/platform/files/test/common/files.test.ts +++ b/src/vs/platform/files/test/common/files.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { isEqual, isEqualOrParent } from 'vs/base/common/extpath'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { toResource } from 'vs/base/test/common/utils'; +import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { FileChangesEvent, FileChangeType, isParent } from 'vs/platform/files/common/files'; suite('Files', () => { @@ -249,4 +249,6 @@ suite('Files', () => { assert(!isEqualOrParent('foo/bar/test.ts', 'foo/BAR/test.', true)); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/files/test/common/watcher.test.ts b/src/vs/platform/files/test/common/watcher.test.ts index 9c20f7693d4..9e964d9dfe8 100644 --- a/src/vs/platform/files/test/common/watcher.test.ts +++ b/src/vs/platform/files/test/common/watcher.test.ts @@ -5,17 +5,21 @@ import * as assert from 'assert'; import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { isEqual } from 'vs/base/common/resources'; import { URI as uri } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { FileChangesEvent, FileChangeType, IFileChange } from 'vs/platform/files/common/files'; import { IDiskFileChange, coalesceEvents, toFileChanges, parseWatcherPatterns } from 'vs/platform/files/common/watcher'; -class TestFileWatcher { +class TestFileWatcher extends Disposable { private readonly _onDidFilesChange: Emitter<{ raw: IFileChange[]; event: FileChangesEvent }>; constructor() { - this._onDidFilesChange = new Emitter<{ raw: IFileChange[]; event: FileChangesEvent }>(); + super(); + + this._onDidFilesChange = this._register(new Emitter<{ raw: IFileChange[]; event: FileChangesEvent }>()); } get onDidFilesChange(): Event<{ raw: IFileChange[]; event: FileChangesEvent }> { @@ -103,12 +107,20 @@ suite('Watcher', () => { assert.strictEqual(parsedPattern('c:\\users\\data\\src\\foo.ts'), false); assert.strictEqual(parsedPattern('c:\\users\\data\\src\\bar\\foo.js'), true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); suite('Watcher Events Normalizer', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('simple add/update/delete', done => { - const watch = new TestFileWatcher(); + const watch = disposables.add(new TestFileWatcher()); const added = uri.file('/users/data/src/added.txt'); const updated = uri.file('/users/data/src/updated.txt'); @@ -120,7 +132,7 @@ suite('Watcher Events Normalizer', () => { { path: deleted.fsPath, type: FileChangeType.DELETED }, ]; - watch.onDidFilesChange(({ event, raw }) => { + disposables.add(watch.onDidFilesChange(({ event, raw }) => { assert.ok(event); assert.strictEqual(raw.length, 3); assert.ok(event.contains(added, FileChangeType.ADDED)); @@ -128,14 +140,14 @@ suite('Watcher Events Normalizer', () => { assert.ok(event.contains(deleted, FileChangeType.DELETED)); done(); - }); + })); watch.report(raw); }); (isWindows ? [Path.WINDOWS, Path.UNC] : [Path.UNIX]).forEach(path => { test(`delete only reported for top level folder (${path})`, done => { - const watch = new TestFileWatcher(); + const watch = disposables.add(new TestFileWatcher()); const deletedFolderA = uri.file(path === Path.UNIX ? '/users/data/src/todelete1' : path === Path.WINDOWS ? 'C:\\users\\data\\src\\todelete1' : '\\\\localhost\\users\\data\\src\\todelete1'); const deletedFolderB = uri.file(path === Path.UNIX ? '/users/data/src/todelete2' : path === Path.WINDOWS ? 'C:\\users\\data\\src\\todelete2' : '\\\\localhost\\users\\data\\src\\todelete2'); @@ -158,7 +170,7 @@ suite('Watcher Events Normalizer', () => { { path: updatedFile.fsPath, type: FileChangeType.UPDATED } ]; - watch.onDidFilesChange(({ event, raw }) => { + disposables.add(watch.onDidFilesChange(({ event, raw }) => { assert.ok(event); assert.strictEqual(raw.length, 5); @@ -169,14 +181,14 @@ suite('Watcher Events Normalizer', () => { assert.ok(event.contains(updatedFile, FileChangeType.UPDATED)); done(); - }); + })); watch.report(raw); }); }); test('event coalescer: ignore CREATE followed by DELETE', done => { - const watch = new TestFileWatcher(); + const watch = disposables.add(new TestFileWatcher()); const created = uri.file('/users/data/src/related'); const deleted = uri.file('/users/data/src/related'); @@ -188,20 +200,20 @@ suite('Watcher Events Normalizer', () => { { path: unrelated.fsPath, type: FileChangeType.UPDATED }, ]; - watch.onDidFilesChange(({ event, raw }) => { + disposables.add(watch.onDidFilesChange(({ event, raw }) => { assert.ok(event); assert.strictEqual(raw.length, 1); assert.ok(event.contains(unrelated, FileChangeType.UPDATED)); done(); - }); + })); watch.report(raw); }); test('event coalescer: flatten DELETE followed by CREATE into CHANGE', done => { - const watch = new TestFileWatcher(); + const watch = disposables.add(new TestFileWatcher()); const deleted = uri.file('/users/data/src/related'); const created = uri.file('/users/data/src/related'); @@ -213,7 +225,7 @@ suite('Watcher Events Normalizer', () => { { path: unrelated.fsPath, type: FileChangeType.UPDATED }, ]; - watch.onDidFilesChange(({ event, raw }) => { + disposables.add(watch.onDidFilesChange(({ event, raw }) => { assert.ok(event); assert.strictEqual(raw.length, 2); @@ -221,13 +233,13 @@ suite('Watcher Events Normalizer', () => { assert.ok(event.contains(unrelated, FileChangeType.UPDATED)); done(); - }); + })); watch.report(raw); }); test('event coalescer: ignore UPDATE when CREATE received', done => { - const watch = new TestFileWatcher(); + const watch = disposables.add(new TestFileWatcher()); const created = uri.file('/users/data/src/related'); const updated = uri.file('/users/data/src/related'); @@ -239,7 +251,7 @@ suite('Watcher Events Normalizer', () => { { path: unrelated.fsPath, type: FileChangeType.UPDATED }, ]; - watch.onDidFilesChange(({ event, raw }) => { + disposables.add(watch.onDidFilesChange(({ event, raw }) => { assert.ok(event); assert.strictEqual(raw.length, 2); @@ -248,13 +260,13 @@ suite('Watcher Events Normalizer', () => { assert.ok(event.contains(unrelated, FileChangeType.UPDATED)); done(); - }); + })); watch.report(raw); }); test('event coalescer: apply DELETE', done => { - const watch = new TestFileWatcher(); + const watch = disposables.add(new TestFileWatcher()); const updated = uri.file('/users/data/src/related'); const updated2 = uri.file('/users/data/src/related'); @@ -268,7 +280,7 @@ suite('Watcher Events Normalizer', () => { { path: updated.fsPath, type: FileChangeType.DELETED } ]; - watch.onDidFilesChange(({ event, raw }) => { + disposables.add(watch.onDidFilesChange(({ event, raw }) => { assert.ok(event); assert.strictEqual(raw.length, 2); @@ -277,13 +289,13 @@ suite('Watcher Events Normalizer', () => { assert.ok(event.contains(unrelated, FileChangeType.UPDATED)); done(); - }); + })); watch.report(raw); }); test('event coalescer: track case renames', done => { - const watch = new TestFileWatcher(); + const watch = disposables.add(new TestFileWatcher()); const oldPath = uri.file('/users/data/src/added'); const newPath = uri.file('/users/data/src/ADDED'); @@ -293,7 +305,7 @@ suite('Watcher Events Normalizer', () => { { path: oldPath.fsPath, type: FileChangeType.DELETED } ]; - watch.onDidFilesChange(({ event, raw }) => { + disposables.add(watch.onDidFilesChange(({ event, raw }) => { assert.ok(event); assert.strictEqual(raw.length, 2); @@ -308,8 +320,10 @@ suite('Watcher Events Normalizer', () => { } done(); - }); + })); watch.report(raw); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts index faebfc38595..3215e9961ec 100644 --- a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts +++ b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts @@ -17,6 +17,7 @@ import { findWindowOnFile } from 'vs/platform/windows/electron-main/windowsFinde import { toWorkspaceFolders } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { FileAccess } from 'vs/base/common/network'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('WindowsFinder', () => { @@ -107,4 +108,6 @@ suite('WindowsFinder', () => { const window: ICodeWindow = createTestCodeWindow({ lastFocusTime: 1, openedWorkspace: testWorkspace }); assert.strictEqual(await findWindowOnFile([window], URI.file(join(fixturesFolder, 'vscode_workspace_2_folder', 'nested_vscode_folder', 'subfolder', 'file.txt')), localWorkspaceResolver), window); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/windows/test/electron-main/windowsStateHandler.test.ts b/src/vs/platform/windows/test/electron-main/windowsStateHandler.test.ts index 60cc320d34f..0b96b1bf740 100644 --- a/src/vs/platform/windows/test/electron-main/windowsStateHandler.test.ts +++ b/src/vs/platform/windows/test/electron-main/windowsStateHandler.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { tmpdir } from 'os'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IWindowState as IWindowUIState, WindowMode } from 'vs/platform/window/electron-main/window'; import { getWindowsStateStoreData, IWindowsState, IWindowState, restoreWindowsState } from 'vs/platform/windows/electron-main/windowsStateHandler'; import { IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; @@ -198,4 +199,6 @@ suite('Windows State Storing', () => { }; assertEqualWindowsState(expected, windowsState, 'v1_32_empty_window'); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts index f75cdcc3f38..2677ffba87d 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts @@ -7,6 +7,7 @@ import * as assert from 'assert'; import { tmpdir } from 'os'; import { join } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFolder, restoreRecentlyOpened, toStoreData } from 'vs/platform/workspaces/common/workspaces'; @@ -143,4 +144,6 @@ suite('History Storage', () => { assertEqualRecentlyOpened(windowsState, expected, 'v1_33'); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts index d6b76495b43..0234ce57bb9 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts @@ -13,6 +13,7 @@ import { isWindows } from 'vs/base/common/platform'; import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; import { IWorkspaceBackupInfo, IFolderBackupInfo } from 'vs/platform/backup/common/backup'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; @@ -357,4 +358,6 @@ flakySuite('WorkspacesManagementMainService', () => { untitled = service.getUntitledWorkspaces(); assert.strictEqual(0, untitled.length); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts b/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts index 594c21561eb..1c92342f5bb 100644 --- a/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts +++ b/src/vs/workbench/contrib/output/test/browser/outputLinkProvider.test.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { isMacintosh, isLinux, isWindows } from 'vs/base/common/platform'; import { OutputLinkComputer } from 'vs/workbench/contrib/output/common/outputLinkComputer'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('OutputLinkProvider', () => { @@ -297,4 +298,6 @@ suite('OutputLinkProvider', () => { assert.ok(res.range.startColumn > 0 && res.range.endColumn > 0); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/label/test/browser/label.test.ts b/src/vs/workbench/services/label/test/browser/label.test.ts index 720417ed0a4..520ed89e35d 100644 --- a/src/vs/workbench/services/label/test/browser/label.test.ts +++ b/src/vs/workbench/services/label/test/browser/label.test.ts @@ -16,6 +16,8 @@ import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage' import { Memento } from 'vs/workbench/common/memento'; import { ResourceLabelFormatter } from 'vs/platform/label/common/label'; import { sep } from 'vs/base/common/path'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('URI Label', () => { let labelService: LabelService; @@ -225,13 +227,14 @@ suite('URI Label', () => { suite('multi-root workspace', () => { let labelService: LabelService; + const disposables = new DisposableStore(); setup(() => { const sources = URI.file('folder1/src'); const tests = URI.file('folder1/test'); const other = URI.file('folder2'); - labelService = new LabelService( + labelService = disposables.add(new LabelService( TestEnvironmentService, new TestContextService( new Workspace('test-workspace', [ @@ -241,9 +244,13 @@ suite('multi-root workspace', () => { ])), new TestPathService(), new TestRemoteAgentService(), - new TestStorageService(), - new TestLifecycleService() - ); + disposables.add(new TestStorageService()), + disposables.add(new TestLifecycleService()) + )); + }); + + teardown(() => { + disposables.clear(); }); test('labels of files in multiroot workspaces are the foldername followed by offset from the folder', () => { @@ -320,7 +327,7 @@ suite('multi-root workspace', () => { test('relative label without formatter', () => { const rootFolder = URI.parse('myscheme://myauthority/'); - labelService = new LabelService( + labelService = disposables.add(new LabelService( TestEnvironmentService, new TestContextService( new Workspace('test-workspace', [ @@ -328,9 +335,9 @@ suite('multi-root workspace', () => { ])), new TestPathService(undefined, rootFolder.scheme), new TestRemoteAgentService(), - new TestStorageService(), - new TestLifecycleService() - ); + disposables.add(new TestStorageService()), + disposables.add(new TestLifecycleService()) + )); const generated = labelService.getUriLabel(URI.parse('myscheme://myauthority/some/folder/test.txt'), { relative: true }); if (isWindows) { @@ -339,6 +346,8 @@ suite('multi-root workspace', () => { assert.strictEqual(generated, 'some/folder/test.txt'); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); suite('workspace at FSP root', () => { @@ -405,4 +414,6 @@ suite('workspace at FSP root', () => { generated = labelService.getUriLabel(URI.parse('myscheme://myauthority/some/folder/test.txt'), { relative: true, separator: '\\' }); assert.strictEqual(generated, 'some\\folder\\test.txt'); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts b/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts index fd0ee28a234..16f3067679c 100644 --- a/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts +++ b/src/vs/workbench/services/progress/test/browser/progressIndicator.test.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { AbstractProgressScope, ScopedProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator'; class TestProgressBar { @@ -63,14 +65,20 @@ class TestProgressBar { suite('Progress Indicator', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('ScopedProgressIndicator', async () => { const testProgressBar = new TestProgressBar(); - const progressScope = new class extends AbstractProgressScope { + const progressScope = disposables.add(new class extends AbstractProgressScope { constructor() { super('test.scopeId', true); } testOnScopeOpened(scopeId: string) { super.onScopeOpened(scopeId); } testOnScopeClosed(scopeId: string): void { super.onScopeClosed(scopeId); } - }(); - const testObject = new ScopedProgressIndicator((testProgressBar), progressScope); + }()); + const testObject = disposables.add(new ScopedProgressIndicator((testProgressBar), progressScope)); // Active: Show (Infinite) let fn = testObject.show(true); @@ -117,4 +125,6 @@ suite('Progress Indicator', () => { progressScope.testOnScopeOpened('test.scopeId'); assert.strictEqual(true, testProgressBar.fDone); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts index c0b6d62e26e..d1a3df96cd2 100644 --- a/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts +++ b/src/vs/workbench/services/textfile/test/browser/textFileEditorModel.test.ts @@ -14,7 +14,7 @@ import { FileOperationResult, FileOperationError } from 'vs/platform/files/commo import { DeferredPromise, timeout } from 'vs/base/common/async'; import { assertIsDefined } from 'vs/base/common/types'; import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { SaveReason, SaveSourceRegistry } from 'vs/workbench/common/editor'; import { isEqual } from 'vs/base/common/resources'; import { UTF16be } from 'vs/workbench/services/textfile/common/encoding'; @@ -38,29 +38,29 @@ suite('Files - TextFileEditorModel', () => { accessor = instantiationService.createInstance(TestServiceAccessor); content = accessor.fileService.getContent(); disposables.add(accessor.textFileService.files); + disposables.add(toDisposable(() => accessor.fileService.setContent(content))); }); - teardown(() => { - accessor.fileService.setContent(content); + teardown(async () => { disposables.clear(); }); test('basic events', async function () { - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + const model = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined)); accessor.workingCopyService.testUnregisterWorkingCopy(model); // causes issues with subsequent resolves otherwise let onDidResolveCounter = 0; - model.onDidResolve(() => onDidResolveCounter++); + disposables.add(model.onDidResolve(() => onDidResolveCounter++)); await model.resolve(); assert.strictEqual(onDidResolveCounter, 1); let onDidChangeContentCounter = 0; - model.onDidChangeContent(() => onDidChangeContentCounter++); + disposables.add(model.onDidChangeContent(() => onDidChangeContentCounter++)); let onDidChangeDirtyCounter = 0; - model.onDidChangeDirty(() => onDidChangeDirtyCounter++); + disposables.add(model.onDidChangeDirty(() => onDidChangeDirtyCounter++)); model.updateTextEditorModel(createTextBufferFactory('bar')); @@ -75,8 +75,6 @@ suite('Files - TextFileEditorModel', () => { await model.revert(); assert.strictEqual(onDidChangeDirtyCounter, 2); - - model.dispose(); }); test('isTextFileEditorModel', async function () { @@ -95,7 +93,7 @@ suite('Files - TextFileEditorModel', () => { assert.strictEqual(accessor.workingCopyService.dirtyCount, 0); let savedEvent: ITextFileEditorModelSaveEvent | undefined = undefined; - model.onDidSave(e => savedEvent = e); + disposables.add(model.onDidSave(e => savedEvent = e)); await model.save(); assert.ok(!savedEvent); @@ -109,11 +107,11 @@ suite('Files - TextFileEditorModel', () => { assert.strictEqual(accessor.workingCopyService.isDirty(model.resource, model.typeId), true); let workingCopyEvent = false; - accessor.workingCopyService.onDidChangeDirty(e => { + disposables.add(accessor.workingCopyService.onDidChangeDirty(e => { if (e.resource.toString() === model.resource.toString()) { workingCopyEvent = true; } - }); + })); const source = SaveSourceRegistry.registerSource('testSource', 'Hello Save'); const pendingSave = model.save({ reason: SaveReason.AUTO, source }); @@ -148,14 +146,14 @@ suite('Files - TextFileEditorModel', () => { await model.resolve(); let savedEvent = false; - model.onDidSave(() => savedEvent = true); + disposables.add(model.onDidSave(() => savedEvent = true)); let workingCopyEvent = false; - accessor.workingCopyService.onDidChangeDirty(e => { + disposables.add(accessor.workingCopyService.onDidChangeDirty(e => { if (e.resource.toString() === model.resource.toString()) { workingCopyEvent = true; } - }); + })); await model.save({ force: true }); @@ -172,10 +170,10 @@ suite('Files - TextFileEditorModel', () => { await model.resolve(); let saveErrorEvent = false; - model.onDidSaveError(() => saveErrorEvent = true); + disposables.add(model.onDidSaveError(() => saveErrorEvent = true)); let savedEvent = false; - model.onDidSave(() => savedEvent = true); + disposables.add(model.onDidSave(() => savedEvent = true)); accessor.fileService.writeShouldThrowError = new Error('failed to write'); try { @@ -229,7 +227,7 @@ suite('Files - TextFileEditorModel', () => { model.updateTextEditorModel(createTextBufferFactory('bar')); let saveErrorEvent = false; - model.onDidSaveError(() => saveErrorEvent = true); + disposables.add(model.onDidSaveError(() => saveErrorEvent = true)); accessor.fileService.writeShouldThrowError = new Error('failed to write'); try { @@ -260,7 +258,7 @@ suite('Files - TextFileEditorModel', () => { model.updateTextEditorModel(createTextBufferFactory('bar')); let saveErrorEvent = false; - model.onDidSaveError(() => saveErrorEvent = true); + disposables.add(model.onDidSaveError(() => saveErrorEvent = true)); accessor.fileService.writeShouldThrowError = new FileOperationError('save conflict', FileOperationResult.FILE_MODIFIED_SINCE); try { @@ -287,7 +285,7 @@ suite('Files - TextFileEditorModel', () => { const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); let encodingEvent = false; - model.onDidChangeEncoding(() => encodingEvent = true); + disposables.add(model.onDidChangeEncoding(() => encodingEvent = true)); await model.setEncoding('utf8', EncodingMode.Encode); // no-op assert.strictEqual(getLastModifiedTime(model), -1); @@ -400,8 +398,8 @@ suite('Files - TextFileEditorModel', () => { const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index.txt'), 'utf8', undefined); assert.ok(model.hasState(TextFileEditorModelState.SAVED)); - model.onDidSave(() => assert.fail()); - model.onDidChangeDirty(() => assert.fail()); + disposables.add(model.onDidSave(() => assert.fail())); + disposables.add(model.onDidChangeDirty(() => assert.fail())); await model.resolve(); assert.ok(model.isResolved()); @@ -447,16 +445,16 @@ suite('Files - TextFileEditorModel', () => { test('Revert', async function () { let eventCounter = 0; - let model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + let model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined)); - model.onDidRevert(() => eventCounter++); + disposables.add(model.onDidRevert(() => eventCounter++)); let workingCopyEvent = false; - accessor.workingCopyService.onDidChangeDirty(e => { + disposables.add(accessor.workingCopyService.onDidChangeDirty(e => { if (e.resource.toString() === model.resource.toString()) { workingCopyEvent = true; } - }); + })); await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); @@ -489,16 +487,16 @@ suite('Files - TextFileEditorModel', () => { test('Revert (soft)', async function () { let eventCounter = 0; - const model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); + const model: TextFileEditorModel = disposables.add(instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined)); - model.onDidRevert(() => eventCounter++); + disposables.add(model.onDidRevert(() => eventCounter++)); let workingCopyEvent = false; - accessor.workingCopyService.onDidChangeDirty(e => { + disposables.add(accessor.workingCopyService.onDidChangeDirty(e => { if (e.resource.toString() === model.resource.toString()) { workingCopyEvent = true; } - }); + })); await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); @@ -559,14 +557,14 @@ suite('Files - TextFileEditorModel', () => { await model.revert({ soft: true }); assert.strictEqual(model.isDirty(), false); - model.onDidChangeDirty(() => eventCounter++); + disposables.add(model.onDidChangeDirty(() => eventCounter++)); let workingCopyEvent = false; - accessor.workingCopyService.onDidChangeDirty(e => { + disposables.add(accessor.workingCopyService.onDidChangeDirty(e => { if (e.resource.toString() === model.resource.toString()) { workingCopyEvent = true; } - }); + })); model.setDirty(true); assert.ok(model.isDirty()); @@ -582,18 +580,18 @@ suite('Files - TextFileEditorModel', () => { test('No Dirty or saving for readonly models', async function () { let workingCopyEvent = false; - accessor.workingCopyService.onDidChangeDirty(e => { + disposables.add(accessor.workingCopyService.onDidChangeDirty(e => { if (e.resource.toString() === model.resource.toString()) { workingCopyEvent = true; } - }); + })); const model = instantiationService.createInstance(TestReadonlyTextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); let saveEvent = false; - model.onDidSave(() => { + disposables.add(model.onDidSave(() => { saveEvent = true; - }); + })); await model.resolve(); model.updateTextEditorModel(createTextBufferFactory('foo')); @@ -682,11 +680,11 @@ suite('Files - TextFileEditorModel', () => { let eventCounter = 0; const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidSave(() => { + disposables.add(model.onDidSave(() => { assert.strictEqual(snapshotToString(model.createSnapshot()!), eventCounter === 1 ? 'bar' : 'foobar'); assert.ok(!model.isDirty()); eventCounter++; - }); + })); const participant = accessor.textFileService.files.addSaveParticipant({ participate: async model => { @@ -738,10 +736,10 @@ suite('Files - TextFileEditorModel', () => { let eventCounter = 0; const model: TextFileEditorModel = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/index_async.txt'), 'utf8', undefined); - model.onDidSave(() => { + disposables.add(model.onDidSave(() => { assert.ok(!model.isDirty()); eventCounter++; - }); + })); const participant = accessor.textFileService.files.addSaveParticipant({ participate: model => { diff --git a/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts b/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts index bcd51283ba6..b3cf625c809 100644 --- a/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts +++ b/src/vs/workbench/services/textfile/test/node/encoding/encoding.test.ts @@ -11,6 +11,7 @@ import { newWriteableBufferStream, VSBuffer, VSBufferReadableStream, streamToBuf import { splitLines } from 'vs/base/common/strings'; import { FileAccess } from 'vs/base/common/network'; import { importAMDNodeModule } from 'vs/amdX'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; export async function detectEncodingByBOM(file: string): Promise { try { @@ -452,4 +453,6 @@ suite('Encoding', () => { assert.strictEqual(iconv.encodingExists(enc), true, enc); } }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts index f96b3de0015..7a00e3208d8 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts @@ -1169,8 +1169,8 @@ export class StoredFileWorkingCopy extend const handle = this.notificationService.notify({ id: `${hash(this.resource.toString())}`, severity: Severity.Error, message, actions: { primary: primaryActions } }); // Remove automatically when we get saved/reverted - const listener = Event.once(Event.any(this.onDidSave, this.onDidRevert))(() => handle.close()); - Event.once(handle.onDidClose)(() => listener.dispose()); + const listener = this._register(Event.once(Event.any(this.onDidSave, this.onDidRevert))(() => handle.close())); + this._register(Event.once(handle.onDidClose)(() => listener.dispose())); } private updateLastResolvedFileStat(newFileStat: IFileStatWithMetadata): void { diff --git a/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts index c745f135603..d4d9097c020 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/resourceWorkingCopy.test.ts @@ -15,6 +15,7 @@ import { ResourceWorkingCopy } from 'vs/workbench/services/workingCopy/common/re import { WorkingCopyCapabilities, IWorkingCopyBackup } from 'vs/workbench/services/workingCopy/common/workingCopy'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('ResourceWorkingCopy', function () { @@ -73,18 +74,19 @@ suite('ResourceWorkingCopy', function () { }); }); - test('dispose, isDisposed', async () => { assert.strictEqual(workingCopy.isDisposed(), false); let disposedEvent = false; - workingCopy.onWillDispose(() => { + disposables.add(workingCopy.onWillDispose(() => { disposedEvent = true; - }); + })); workingCopy.dispose(); assert.strictEqual(workingCopy.isDisposed(), true); assert.strictEqual(disposedEvent, true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts index 8d45d3c7ddf..f87ad8050e8 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopy.test.ts @@ -18,6 +18,7 @@ import { SaveReason, SaveSourceRegistry } from 'vs/workbench/common/editor'; import { Promises, timeout } from 'vs/base/common/async'; import { consumeReadable, consumeStream, isReadableStream } from 'vs/base/common/stream'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; export class TestStoredFileWorkingCopyModel extends Disposable implements IStoredFileWorkingCopyModel { @@ -130,22 +131,17 @@ suite('StoredFileWorkingCopy (with custom save)', function () { const factory = new TestStoredFileWorkingCopyModelWithCustomSaveFactory(); const disposables = new DisposableStore(); - const resource = URI.file('test/resource'); + let instantiationService: IInstantiationService; let accessor: TestServiceAccessor; let workingCopy: StoredFileWorkingCopy; - function createWorkingCopy(uri: URI = resource) { - const workingCopy: StoredFileWorkingCopy = new StoredFileWorkingCopy('testStoredFileWorkingCopyType', uri, basename(uri), factory, options => workingCopy.resolve(options), accessor.fileService, accessor.logService, accessor.workingCopyFileService, accessor.filesConfigurationService, accessor.workingCopyBackupService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService); - - return workingCopy; - } - setup(() => { instantiationService = workbenchInstantiationService(undefined, disposables); accessor = instantiationService.createInstance(TestServiceAccessor); - workingCopy = disposables.add(createWorkingCopy()); + const resource = URI.file('test/resource'); + workingCopy = disposables.add(new StoredFileWorkingCopy('testStoredFileWorkingCopyType', resource, basename(resource), factory, options => workingCopy.resolve(options), accessor.fileService, accessor.logService, accessor.workingCopyFileService, accessor.filesConfigurationService, accessor.workingCopyBackupService, accessor.workingCopyService, accessor.notificationService, accessor.workingCopyEditorService, accessor.editorService, accessor.elevatedFileService)); }); teardown(() => { @@ -155,15 +151,15 @@ suite('StoredFileWorkingCopy (with custom save)', function () { test('save (custom implemented)', async () => { let savedCounter = 0; let lastSaveEvent: IStoredFileWorkingCopySaveEvent | undefined = undefined; - workingCopy.onDidSave(e => { + disposables.add(workingCopy.onDidSave(e => { savedCounter++; lastSaveEvent = e; - }); + })); let saveErrorCounter = 0; - workingCopy.onDidSaveError(() => { + disposables.add(workingCopy.onDidSaveError(() => { saveErrorCounter++; - }); + })); // unresolved await workingCopy.save(); @@ -192,6 +188,8 @@ suite('StoredFileWorkingCopy (with custom save)', function () { assert.strictEqual(saveErrorCounter, 1); assert.strictEqual(workingCopy.hasState(StoredFileWorkingCopyState.ERROR), true); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); suite('StoredFileWorkingCopy', function () { diff --git a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts index 08665525ef0..4a63529c127 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/storedFileWorkingCopyManager.test.ts @@ -90,19 +90,19 @@ suite('StoredFileWorkingCopyManager', () => { test('resolve (async)', async () => { const resource = URI.file('/path/index.txt'); - await manager.resolve(resource); + disposables.add(await manager.resolve(resource)); let didResolve = false; let onDidResolve = new Promise(resolve => { - manager.onDidResolve(({ model }) => { + disposables.add(manager.onDidResolve(({ model }) => { if (model?.resource.toString() === resource.toString()) { didResolve = true; resolve(); } - }); + })); }); - manager.resolve(resource, { reload: { async: true } }); + const resolve = manager.resolve(resource, { reload: { async: true } }); await onDidResolve; @@ -111,12 +111,12 @@ suite('StoredFileWorkingCopyManager', () => { didResolve = false; onDidResolve = new Promise(resolve => { - manager.onDidResolve(({ model }) => { + disposables.add(manager.onDidResolve(({ model }) => { if (model?.resource.toString() === resource.toString()) { didResolve = true; resolve(); } - }); + })); }); manager.resolve(resource, { reload: { async: true, force: true } }); @@ -124,6 +124,8 @@ suite('StoredFileWorkingCopyManager', () => { await onDidResolve; assert.strictEqual(didResolve, true); + + disposables.add(await resolve); }); test('resolve (sync)', async () => { @@ -132,18 +134,18 @@ suite('StoredFileWorkingCopyManager', () => { await manager.resolve(resource); let didResolve = false; - manager.onDidResolve(({ model }) => { + disposables.add(manager.onDidResolve(({ model }) => { if (model?.resource.toString() === resource.toString()) { didResolve = true; } - }); + })); - await manager.resolve(resource, { reload: { async: false } }); + disposables.add(await manager.resolve(resource, { reload: { async: false } })); assert.strictEqual(didResolve, true); didResolve = false; - await manager.resolve(resource, { reload: { async: false, force: true } }); + disposables.add(await manager.resolve(resource, { reload: { async: false, force: true } })); assert.strictEqual(didResolve, true); }); @@ -170,7 +172,7 @@ suite('StoredFileWorkingCopyManager', () => { test('resolve (sync) - model not disposed when error and model existed before', async () => { const resource = URI.file('/path/index.txt'); - await manager.resolve(resource); + disposables.add(await manager.resolve(resource)); accessor.fileService.readShouldThrowError = new FileOperationError('fail', FileOperationResult.FILE_OTHER_ERROR); @@ -272,23 +274,23 @@ suite('StoredFileWorkingCopyManager', () => { let savedCounter = 0; let saveErrorCounter = 0; - manager.onDidCreate(() => { + disposables.add(manager.onDidCreate(() => { createdCounter++; - }); + })); - manager.onDidRemove(resource => { + disposables.add(manager.onDidRemove(resource => { if (resource.toString() === resource1.toString() || resource.toString() === resource2.toString()) { removedCounter++; } - }); + })); - manager.onDidResolve(workingCopy => { + disposables.add(manager.onDidResolve(workingCopy => { if (workingCopy.resource.toString() === resource1.toString()) { resolvedCounter++; } - }); + })); - manager.onDidChangeDirty(workingCopy => { + disposables.add(manager.onDidChangeDirty(workingCopy => { if (workingCopy.resource.toString() === resource1.toString()) { if (workingCopy.isDirty()) { gotDirtyCounter++; @@ -296,36 +298,36 @@ suite('StoredFileWorkingCopyManager', () => { gotNonDirtyCounter++; } } - }); + })); - manager.onDidRevert(workingCopy => { + disposables.add(manager.onDidRevert(workingCopy => { if (workingCopy.resource.toString() === resource1.toString()) { revertedCounter++; } - }); + })); let lastSaveEvent: IStoredFileWorkingCopySaveEvent | undefined = undefined; - manager.onDidSave((e) => { + disposables.add(manager.onDidSave((e) => { if (e.workingCopy.resource.toString() === resource1.toString()) { lastSaveEvent = e; savedCounter++; } - }); + })); - manager.onDidSaveError(workingCopy => { + disposables.add(manager.onDidSaveError(workingCopy => { if (workingCopy.resource.toString() === resource1.toString()) { saveErrorCounter++; } - }); + })); - const workingCopy1 = await manager.resolve(resource1); + const workingCopy1 = disposables.add(await manager.resolve(resource1)); assert.strictEqual(resolvedCounter, 1); assert.strictEqual(createdCounter, 1); accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.DELETED }], false)); accessor.fileService.fireFileChanges(new FileChangesEvent([{ resource: resource1, type: FileChangeType.ADDED }], false)); - const workingCopy2 = await manager.resolve(resource2); + const workingCopy2 = disposables.add(await manager.resolve(resource2)); assert.strictEqual(resolvedCounter, 2); assert.strictEqual(createdCounter, 2); @@ -620,10 +622,10 @@ suite('StoredFileWorkingCopyManager', () => { const resource1 = URI.file('/path/index_something1.txt'); const resource2 = URI.file('/path/index_something2.txt'); - const workingCopy1 = await manager.resolve(resource1); + const workingCopy1 = disposables.add(await manager.resolve(resource1)); workingCopy1.model?.updateContents('make dirty'); - const workingCopy2 = await manager.resolve(resource2); + const workingCopy2 = disposables.add(await manager.resolve(resource2)); workingCopy2.model?.updateContents('make dirty'); let saved1 = false; diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts index d38d34ce846..94e3c941233 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test.ts @@ -12,6 +12,7 @@ import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/resources'; import { consumeReadable, consumeStream, isReadable, isReadableStream } from 'vs/base/common/stream'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUntitledFileWorkingCopyModel, IUntitledFileWorkingCopyModelContentChangedEvent, IUntitledFileWorkingCopyModelFactory, UntitledFileWorkingCopy } from 'vs/workbench/services/workingCopy/common/untitledFileWorkingCopy'; import { TestServiceAccessor, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; @@ -97,7 +98,7 @@ suite('UntitledFileWorkingCopy', () => { let workingCopy: UntitledFileWorkingCopy; function createWorkingCopy(uri: URI = resource, hasAssociatedFilePath = false, initialValue = '') { - return new UntitledFileWorkingCopy( + return disposables.add(new UntitledFileWorkingCopy( 'testUntitledWorkingCopyType', uri, basename(uri), @@ -109,7 +110,7 @@ suite('UntitledFileWorkingCopy', () => { accessor.workingCopyService, accessor.workingCopyBackupService, accessor.logService - ); + )); } setup(() => { @@ -135,14 +136,14 @@ suite('UntitledFileWorkingCopy', () => { assert.strictEqual(workingCopy.isDirty(), false); let changeDirtyCounter = 0; - workingCopy.onDidChangeDirty(() => { + disposables.add(workingCopy.onDidChangeDirty(() => { changeDirtyCounter++; - }); + })); let contentChangeCounter = 0; - workingCopy.onDidChangeContent(() => { + disposables.add(workingCopy.onDidChangeContent(() => { contentChangeCounter++; - }); + })); await workingCopy.resolve(); assert.strictEqual(workingCopy.isResolved(), true); @@ -189,14 +190,14 @@ suite('UntitledFileWorkingCopy', () => { test('revert', async () => { let revertCounter = 0; - workingCopy.onDidRevert(() => { + disposables.add(workingCopy.onDidRevert(() => { revertCounter++; - }); + })); let disposeCounter = 0; - workingCopy.onWillDispose(() => { + disposables.add(workingCopy.onWillDispose(() => { disposeCounter++; - }); + })); await workingCopy.resolve(); @@ -212,9 +213,9 @@ suite('UntitledFileWorkingCopy', () => { test('dispose', async () => { let disposeCounter = 0; - workingCopy.onWillDispose(() => { + disposables.add(workingCopy.onWillDispose(() => { disposeCounter++; - }); + })); await workingCopy.resolve(); workingCopy.dispose(); @@ -257,9 +258,9 @@ suite('UntitledFileWorkingCopy', () => { workingCopy = createWorkingCopy(resource, false, 'Hello Initial'); let contentChangeCounter = 0; - workingCopy.onDidChangeContent(() => { + disposables.add(workingCopy.onDidChangeContent(() => { contentChangeCounter++; - }); + })); assert.strictEqual(workingCopy.isDirty(), true); @@ -319,9 +320,9 @@ suite('UntitledFileWorkingCopy', () => { workingCopy = createWorkingCopy(); let contentChangeCounter = 0; - workingCopy.onDidChangeContent(() => { + disposables.add(workingCopy.onDidChangeContent(() => { contentChangeCounter++; - }); + })); await workingCopy.resolve(); @@ -329,4 +330,6 @@ suite('UntitledFileWorkingCopy', () => { assert.strictEqual(workingCopy.model?.contents, 'Hello Backup'); assert.strictEqual(contentChangeCounter, 1); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts b/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts index 501dd4dac49..a04a7c4951a 100644 --- a/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts +++ b/src/vs/workbench/services/workingCopy/test/browser/untitledScratchpadWorkingCopy.test.ts @@ -11,6 +11,7 @@ import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/resources'; import { consumeReadable, consumeStream, isReadable, isReadableStream } from 'vs/base/common/stream'; import { URI } from 'vs/base/common/uri'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUntitledFileWorkingCopyModelFactory, UntitledFileWorkingCopy } from 'vs/workbench/services/workingCopy/common/untitledFileWorkingCopy'; import { TestUntitledFileWorkingCopyModel } from 'vs/workbench/services/workingCopy/test/browser/untitledFileWorkingCopy.test'; @@ -34,7 +35,7 @@ suite('UntitledScratchpadWorkingCopy', () => { let workingCopy: UntitledFileWorkingCopy; function createWorkingCopy(uri: URI = resource, hasAssociatedFilePath = false, initialValue = '') { - return new UntitledFileWorkingCopy( + return disposables.add(new UntitledFileWorkingCopy( 'testUntitledWorkingCopyType', uri, basename(uri), @@ -46,7 +47,7 @@ suite('UntitledScratchpadWorkingCopy', () => { accessor.workingCopyService, accessor.workingCopyBackupService, accessor.logService - ); + )); } setup(() => { @@ -72,14 +73,14 @@ suite('UntitledScratchpadWorkingCopy', () => { assert.strictEqual(workingCopy.isDirty(), false); let changeDirtyCounter = 0; - workingCopy.onDidChangeDirty(() => { + disposables.add(workingCopy.onDidChangeDirty(() => { changeDirtyCounter++; - }); + })); let contentChangeCounter = 0; - workingCopy.onDidChangeContent(() => { + disposables.add(workingCopy.onDidChangeContent(() => { contentChangeCounter++; - }); + })); await workingCopy.resolve(); assert.strictEqual(workingCopy.isResolved(), true); @@ -128,14 +129,14 @@ suite('UntitledScratchpadWorkingCopy', () => { test('revert', async () => { let revertCounter = 0; - workingCopy.onDidRevert(() => { + disposables.add(workingCopy.onDidRevert(() => { revertCounter++; - }); + })); let disposeCounter = 0; - workingCopy.onWillDispose(() => { + disposables.add(workingCopy.onWillDispose(() => { disposeCounter++; - }); + })); await workingCopy.resolve(); @@ -151,9 +152,9 @@ suite('UntitledScratchpadWorkingCopy', () => { test('dispose', async () => { let disposeCounter = 0; - workingCopy.onWillDispose(() => { + disposables.add(workingCopy.onWillDispose(() => { disposeCounter++; - }); + })); await workingCopy.resolve(); workingCopy.dispose(); @@ -196,9 +197,9 @@ suite('UntitledScratchpadWorkingCopy', () => { workingCopy = createWorkingCopy(resource, false, 'Hello Initial'); let contentChangeCounter = 0; - workingCopy.onDidChangeContent(() => { + disposables.add(workingCopy.onDidChangeContent(() => { contentChangeCounter++; - }); + })); assert.strictEqual(workingCopy.isModified(), true); @@ -258,9 +259,9 @@ suite('UntitledScratchpadWorkingCopy', () => { workingCopy = createWorkingCopy(); let contentChangeCounter = 0; - workingCopy.onDidChangeContent(() => { + disposables.add(workingCopy.onDidChangeContent(() => { contentChangeCounter++; - }); + })); await workingCopy.resolve(); @@ -268,4 +269,6 @@ suite('UntitledScratchpadWorkingCopy', () => { assert.strictEqual(workingCopy.model?.contents, 'Hello Backup'); assert.strictEqual(contentChangeCounter, 1); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts index 81f856c6d9d..384e3b17955 100644 --- a/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/common/workingCopyService.test.ts @@ -8,26 +8,34 @@ import { IWorkingCopy } from 'vs/workbench/services/workingCopy/common/workingCo import { URI } from 'vs/base/common/uri'; import { TestWorkingCopy } from 'vs/workbench/test/common/workbenchTestServices'; import { IWorkingCopySaveEvent, WorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { DisposableStore } from 'vs/base/common/lifecycle'; suite('WorkingCopyService', () => { + const disposables = new DisposableStore(); + + teardown(() => { + disposables.clear(); + }); + test('registry - basics', () => { - const service = new WorkingCopyService(); + const service = disposables.add(new WorkingCopyService()); const onDidChangeDirty: IWorkingCopy[] = []; - service.onDidChangeDirty(copy => onDidChangeDirty.push(copy)); + disposables.add(service.onDidChangeDirty(copy => onDidChangeDirty.push(copy))); const onDidChangeContent: IWorkingCopy[] = []; - service.onDidChangeContent(copy => onDidChangeContent.push(copy)); + disposables.add(service.onDidChangeContent(copy => onDidChangeContent.push(copy))); const onDidSave: IWorkingCopySaveEvent[] = []; - service.onDidSave(copy => onDidSave.push(copy)); + disposables.add(service.onDidSave(copy => onDidSave.push(copy))); const onDidRegister: IWorkingCopy[] = []; - service.onDidRegister(copy => onDidRegister.push(copy)); + disposables.add(service.onDidRegister(copy => onDidRegister.push(copy))); const onDidUnregister: IWorkingCopy[] = []; - service.onDidUnregister(copy => onDidUnregister.push(copy)); + disposables.add(service.onDidUnregister(copy => onDidUnregister.push(copy))); assert.strictEqual(service.hasDirty, false); assert.strictEqual(service.dirtyCount, 0); @@ -40,7 +48,7 @@ suite('WorkingCopyService', () => { assert.strictEqual(service.has({ resource: resource1, typeId: 'testWorkingCopyType' }), false); assert.strictEqual(service.get({ resource: resource1, typeId: 'testWorkingCopyType' }), undefined); assert.strictEqual(service.getAll(resource1), undefined); - const copy1 = new TestWorkingCopy(resource1); + const copy1 = disposables.add(new TestWorkingCopy(resource1)); const unregister1 = service.registerWorkingCopy(copy1); assert.strictEqual(service.workingCopies.length, 1); @@ -100,7 +108,7 @@ suite('WorkingCopyService', () => { // resource 2 const resource2 = URI.file('/some/folder/file-dirty.txt'); - const copy2 = new TestWorkingCopy(resource2, true); + const copy2 = disposables.add(new TestWorkingCopy(resource2, true)); const unregister2 = service.registerWorkingCopy(copy2); assert.strictEqual(onDidRegister.length, 2); @@ -128,33 +136,33 @@ suite('WorkingCopyService', () => { }); test('registry - multiple copies on same resource throws (same type ID)', () => { - const service = new WorkingCopyService(); + const service = disposables.add(new WorkingCopyService()); const resource = URI.parse('custom://some/folder/custom.txt'); - const copy1 = new TestWorkingCopy(resource); - service.registerWorkingCopy(copy1); + const copy1 = disposables.add(new TestWorkingCopy(resource)); + disposables.add(service.registerWorkingCopy(copy1)); - const copy2 = new TestWorkingCopy(resource); + const copy2 = disposables.add(new TestWorkingCopy(resource)); assert.throws(() => service.registerWorkingCopy(copy2)); }); test('registry - multiple copies on same resource is supported (different type ID)', () => { - const service = new WorkingCopyService(); + const service = disposables.add(new WorkingCopyService()); const resource = URI.parse('custom://some/folder/custom.txt'); const typeId1 = 'testWorkingCopyTypeId1'; - let copy1 = new TestWorkingCopy(resource, false, typeId1); + let copy1 = disposables.add(new TestWorkingCopy(resource, false, typeId1)); let dispose1 = service.registerWorkingCopy(copy1); const typeId2 = 'testWorkingCopyTypeId2'; - const copy2 = new TestWorkingCopy(resource, false, typeId2); + const copy2 = disposables.add(new TestWorkingCopy(resource, false, typeId2)); const dispose2 = service.registerWorkingCopy(copy2); const typeId3 = 'testWorkingCopyTypeId3'; - const copy3 = new TestWorkingCopy(resource, false, typeId3); + const copy3 = disposables.add(new TestWorkingCopy(resource, false, typeId3)); const dispose3 = service.registerWorkingCopy(copy3); const copies = service.getAll(resource); @@ -196,7 +204,7 @@ suite('WorkingCopyService', () => { assert.strictEqual(service.isDirty(resource, typeId3), false); dispose1.dispose(); - copy1 = new TestWorkingCopy(resource, false, typeId1); + copy1 = disposables.add(new TestWorkingCopy(resource, false, typeId1)); dispose1 = service.registerWorkingCopy(copy1); dispose1.dispose(); @@ -205,4 +213,6 @@ suite('WorkingCopyService', () => { assert.strictEqual(service.workingCopies.length, 0); }); + + ensureNoDisposablesAreLeakedInTestSuite(); }); From ba0ea792a526028977e14bc8c7fd65221db53f38 Mon Sep 17 00:00:00 2001 From: rebornix Date: Thu, 7 Sep 2023 08:28:59 -0700 Subject: [PATCH 604/607] Re #190503. Dispose components in editor/keyboard/list tests. --- .../test/browser/ui/list/listWidget.test.ts | 5 + .../pieceTreeTextBuffer.test.ts | 384 +++++++++++++----- .../browser/browserKeyboardMapper.test.ts | 10 +- 3 files changed, 303 insertions(+), 96 deletions(-) diff --git a/src/vs/base/test/browser/ui/list/listWidget.test.ts b/src/vs/base/test/browser/ui/list/listWidget.test.ts index bb533961b2d..995445f3500 100644 --- a/src/vs/base/test/browser/ui/list/listWidget.test.ts +++ b/src/vs/base/test/browser/ui/list/listWidget.test.ts @@ -8,8 +8,11 @@ import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/lis import { List } from 'vs/base/browser/ui/list/listWidget'; import { range } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; suite('ListWidget', function () { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('Page up and down', async function () { const element = document.createElement('div'); element.style.height = '200px'; @@ -30,6 +33,7 @@ suite('ListWidget', function () { }; const listWidget = new List('test', element, delegate, [renderer]); + ds.add(listWidget); listWidget.layout(200); assert.strictEqual(templatesCount, 0, 'no templates have been allocated'); @@ -75,6 +79,7 @@ suite('ListWidget', function () { }; const listWidget = new List('test', element, delegate, [renderer]); + ds.add(listWidget); listWidget.layout(200); assert.strictEqual(templatesCount, 0, 'no templates have been allocated'); diff --git a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts index f1d41730546..262032dd9e4 100644 --- a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts @@ -14,6 +14,7 @@ import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeText import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { splitLines } from 'vs/base/common/strings'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n'; @@ -151,13 +152,13 @@ function testLineStarts(str: string, pieceTable: PieceTreeBase) { } } -function createTextBuffer(val: string[], normalizeEOL: boolean = true): PieceTreeBase { +function createTextBuffer(val: string[], normalizeEOL: boolean = true): PieceTreeTextBuffer { const bufferBuilder = new PieceTreeTextBufferBuilder(); for (const chunk of val) { bufferBuilder.acceptChunk(chunk); } const factory = bufferBuilder.finish(normalizeEOL); - return (factory.create(DefaultEndOfLine.LF).textBuffer).getPieceTree(); + return (factory.create(DefaultEndOfLine.LF).textBuffer); } function assertTreeInvariants(T: PieceTreeBase): void { @@ -212,10 +213,14 @@ function assertValidTree(T: PieceTreeBase): void { //#endregion suite('inserts and deletes', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('basic insert/delete', () => { - const pieceTable = createTextBuffer([ + const pieceTree = createTextBuffer([ 'This is a document with some text.' ]); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(34, 'This is some more text to insert at offset 34.'); assert.strictEqual( @@ -231,8 +236,9 @@ suite('inserts and deletes', () => { }); test('more inserts', () => { - const pt = createTextBuffer(['']); - + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pt = pieceTree.getPieceTree(); pt.insert(0, 'AAA'); assert.strictEqual(pt.getLinesRawContent(), 'AAA'); pt.insert(0, 'BBB'); @@ -245,7 +251,10 @@ suite('inserts and deletes', () => { }); test('more deletes', () => { - const pt = createTextBuffer(['012345678']); + const pieceTree = createTextBuffer(['012345678']); + ds.add(pieceTree); + const pt = pieceTree.getPieceTree(); + pt.delete(8, 1); assert.strictEqual(pt.getLinesRawContent(), '01234567'); pt.delete(0, 1); @@ -261,7 +270,10 @@ suite('inserts and deletes', () => { test('random test 1', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + pieceTable.insert(0, 'ceLPHmFzvCtFeHkCBej '); str = str.substring(0, 0) + 'ceLPHmFzvCtFeHkCBej ' + str.substring(0); assert.strictEqual(pieceTable.getLinesRawContent(), str); @@ -280,7 +292,9 @@ suite('inserts and deletes', () => { test('random test 2', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'VgPG '); str = str.substring(0, 0) + 'VgPG ' + str.substring(0); pieceTable.insert(2, 'DdWF '); @@ -298,7 +312,9 @@ suite('inserts and deletes', () => { test('random test 3', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'gYSz'); str = str.substring(0, 0) + 'gYSz' + str.substring(0); pieceTable.insert(1, 'mDQe'); @@ -314,7 +330,9 @@ suite('inserts and deletes', () => { test('random delete 1', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'vfb'); str = str.substring(0, 0) + 'vfb' + str.substring(0); @@ -347,7 +365,9 @@ suite('inserts and deletes', () => { test('random delete 2', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'IDT'); str = str.substring(0, 0) + 'IDT' + str.substring(0); @@ -373,7 +393,9 @@ suite('inserts and deletes', () => { test('random delete 3', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'PqM'); str = str.substring(0, 0) + 'PqM' + str.substring(0); pieceTable.delete(1, 2); @@ -406,7 +428,9 @@ suite('inserts and deletes', () => { test('random insert/delete \\r bug 1', () => { let str = 'a'; - const pieceTable = createTextBuffer(['a']); + const pieceTree = createTextBuffer(['a']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(0, 1); str = str.substring(0, 0) + str.substring(0 + 1); pieceTable.insert(0, '\r\r\n\n'); @@ -432,7 +456,9 @@ suite('inserts and deletes', () => { test('random insert/delete \\r bug 2', () => { let str = 'a'; - const pieceTable = createTextBuffer(['a']); + const pieceTree = createTextBuffer(['a']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(1, '\naa\r'); str = str.substring(0, 1) + '\naa\r' + str.substring(1); pieceTable.delete(0, 4); @@ -460,7 +486,9 @@ suite('inserts and deletes', () => { test('random insert/delete \\r bug 3', () => { let str = 'a'; - const pieceTable = createTextBuffer(['a']); + const pieceTree = createTextBuffer(['a']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\r\na\r'); str = str.substring(0, 0) + '\r\na\r' + str.substring(0); pieceTable.delete(2, 3); @@ -489,7 +517,9 @@ suite('inserts and deletes', () => { test('random insert/delete \\r bug 4s', () => { let str = 'a'; - const pieceTable = createTextBuffer(['a']); + const pieceTree = createTextBuffer(['a']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(0, 1); str = str.substring(0, 0) + str.substring(0 + 1); pieceTable.insert(0, '\naaa'); @@ -516,7 +546,9 @@ suite('inserts and deletes', () => { }); test('random insert/delete \\r bug 5', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\n\n\r'); str = str.substring(0, 0) + '\n\n\n\r' + str.substring(0); pieceTable.insert(1, '\n\n\n\r'); @@ -544,8 +576,12 @@ suite('inserts and deletes', () => { }); suite('prefix sum for line feed', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('basic', () => { - const pieceTable = createTextBuffer(['1\n2\n3\n4']); + const pieceTree = createTextBuffer(['1\n2\n3\n4']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); assert.strictEqual(pieceTable.getLineCount(), 4); assert.deepStrictEqual(pieceTable.getPositionAt(0), new Position(1, 1)); @@ -567,7 +603,9 @@ suite('prefix sum for line feed', () => { }); test('append', () => { - const pieceTable = createTextBuffer(['a\nb\nc\nde']); + const pieceTree = createTextBuffer(['a\nb\nc\nde']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(8, 'fh\ni\njk'); assert.strictEqual(pieceTable.getLineCount(), 6); @@ -577,7 +615,9 @@ suite('prefix sum for line feed', () => { }); test('insert', () => { - const pieceTable = createTextBuffer(['a\nb\nc\nde']); + const pieceTree = createTextBuffer(['a\nb\nc\nde']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(7, 'fh\ni\njk'); assert.strictEqual(pieceTable.getLineCount(), 6); @@ -600,7 +640,10 @@ suite('prefix sum for line feed', () => { }); test('delete', () => { - const pieceTable = createTextBuffer(['a\nb\nc\ndefh\ni\njk']); + const pieceTree = createTextBuffer(['a\nb\nc\ndefh\ni\njk']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + pieceTable.delete(7, 2); assert.strictEqual(pieceTable.getLinesRawContent(), 'a\nb\nc\ndh\ni\njk'); @@ -624,7 +667,9 @@ suite('prefix sum for line feed', () => { }); test('add+delete 1', () => { - const pieceTable = createTextBuffer(['a\nb\nc\nde']); + const pieceTree = createTextBuffer(['a\nb\nc\nde']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(8, 'fh\ni\njk'); pieceTable.delete(7, 2); @@ -650,7 +695,9 @@ suite('prefix sum for line feed', () => { test('insert random bug 1: prefixSumComputer.removeValues(start, cnt) cnt is 1 based.', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, ' ZX \n Z\nZ\n YZ\nY\nZXX '); str = str.substring(0, 0) + @@ -667,7 +714,9 @@ suite('prefix sum for line feed', () => { test('insert random bug 2: prefixSumComputer initialize does not do deep copy of UInt32Array.', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'ZYZ\nYY XY\nX \nZ Y \nZ '); str = str.substring(0, 0) + 'ZYZ\nYY XY\nX \nZ Y \nZ ' + str.substring(0); @@ -680,7 +729,9 @@ suite('prefix sum for line feed', () => { }); test('delete random bug 1: I forgot to update the lineFeedCnt when deletion is on one single piece.', () => { - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'ba\na\nca\nba\ncbab\ncaa '); pieceTable.insert(13, 'cca\naabb\ncac\nccc\nab '); pieceTable.delete(5, 8); @@ -709,7 +760,9 @@ suite('prefix sum for line feed', () => { test('delete random bug rb tree 1', () => { let str = ''; - const pieceTable = createTextBuffer([str]); + const pieceTree = createTextBuffer([str]); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'YXXZ\n\nYY\n'); str = str.substring(0, 0) + 'YXXZ\n\nYY\n' + str.substring(0); pieceTable.delete(0, 5); @@ -724,7 +777,9 @@ suite('prefix sum for line feed', () => { test('delete random bug rb tree 2', () => { let str = ''; - const pieceTable = createTextBuffer([str]); + const pieceTree = createTextBuffer([str]); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'YXXZ\n\nYY\n'); str = str.substring(0, 0) + 'YXXZ\n\nYY\n' + str.substring(0); pieceTable.insert(0, 'ZXYY\nX\nZ\n'); @@ -744,7 +799,9 @@ suite('prefix sum for line feed', () => { test('delete random bug rb tree 3', () => { let str = ''; - const pieceTable = createTextBuffer([str]); + const pieceTree = createTextBuffer([str]); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'YXXZ\n\nYY\n'); str = str.substring(0, 0) + 'YXXZ\n\nYY\n' + str.substring(0); pieceTable.delete(7, 2); @@ -772,9 +829,13 @@ suite('prefix sum for line feed', () => { }); suite('offset 2 position', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('random tests bug 1', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'huuyYzUfKOENwGgZLqn '); str = str.substring(0, 0) + 'huuyYzUfKOENwGgZLqn ' + str.substring(0); pieceTable.delete(18, 2); @@ -796,8 +857,12 @@ suite('offset 2 position', () => { }); suite('get text in range', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('getContentInRange', () => { - const pieceTable = createTextBuffer(['a\nb\nc\nde']); + const pieceTree = createTextBuffer(['a\nb\nc\nde']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(8, 'fh\ni\njk'); pieceTable.delete(7, 2); // 'a\nb\nc\ndh\ni\njk' @@ -813,7 +878,9 @@ suite('get text in range', () => { test('random test value in range', () => { let str = ''; - const pieceTable = createTextBuffer([str]); + const pieceTree = createTextBuffer([str]); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'ZXXY'); str = str.substring(0, 0) + 'ZXXY' + str.substring(0); @@ -831,7 +898,9 @@ suite('get text in range', () => { }); test('random test value in range exception', () => { let str = ''; - const pieceTable = createTextBuffer([str]); + const pieceTree = createTextBuffer([str]); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'XZ\nZ'); str = str.substring(0, 0) + 'XZ\nZ' + str.substring(0); @@ -850,7 +919,9 @@ suite('get text in range', () => { test('random tests bug 1', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'huuyYzUfKOENwGgZLqn '); str = str.substring(0, 0) + 'huuyYzUfKOENwGgZLqn ' + str.substring(0); pieceTable.delete(18, 2); @@ -871,7 +942,9 @@ suite('get text in range', () => { test('random tests bug 2', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'xfouRDZwdAHjVXJAMV\n '); str = str.substring(0, 0) + 'xfouRDZwdAHjVXJAMV\n ' + str.substring(0); pieceTable.insert(16, 'dBGndxpFZBEAIKykYYx '); @@ -900,7 +973,10 @@ suite('get text in range', () => { }); test('get line content', () => { - const pieceTable = createTextBuffer(['1']); + const pieceTree = createTextBuffer(['1']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + assert.strictEqual(pieceTable.getLineRawContent(1), '1'); pieceTable.insert(1, '2'); assert.strictEqual(pieceTable.getLineRawContent(1), '12'); @@ -908,7 +984,10 @@ suite('get text in range', () => { }); test('get line content basic', () => { - const pieceTable = createTextBuffer(['1\n2\n3\n4']); + const pieceTree = createTextBuffer(['1\n2\n3\n4']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + assert.strictEqual(pieceTable.getLineRawContent(1), '1\n'); assert.strictEqual(pieceTable.getLineRawContent(2), '2\n'); assert.strictEqual(pieceTable.getLineRawContent(3), '3\n'); @@ -917,7 +996,9 @@ suite('get text in range', () => { }); test('get line content after inserts/deletes', () => { - const pieceTable = createTextBuffer(['a\nb\nc\nde']); + const pieceTree = createTextBuffer(['a\nb\nc\nde']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(8, 'fh\ni\njk'); pieceTable.delete(7, 2); // 'a\nb\nc\ndh\ni\njk' @@ -933,7 +1014,9 @@ suite('get text in range', () => { test('random 1', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'J eNnDzQpnlWyjmUu\ny '); str = str.substring(0, 0) + 'J eNnDzQpnlWyjmUu\ny ' + str.substring(0); @@ -948,7 +1031,9 @@ suite('get text in range', () => { test('random 2', () => { let str = ''; - const pieceTable = createTextBuffer(['']); + const pieceTree = createTextBuffer(['']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'DZoQ tglPCRHMltejRI '); str = str.substring(0, 0) + 'DZoQ tglPCRHMltejRI ' + str.substring(0); pieceTable.insert(10, 'JRXiyYqJ qqdcmbfkKX '); @@ -967,8 +1052,12 @@ suite('get text in range', () => { }); suite('CRLF', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('delete CR in CRLF 1', () => { - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'a\r\nb'); pieceTable.delete(0, 2); @@ -977,7 +1066,9 @@ suite('CRLF', () => { }); test('delete CR in CRLF 2', () => { - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'a\r\nb'); pieceTable.delete(2, 2); @@ -987,7 +1078,9 @@ suite('CRLF', () => { test('random bug 1', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\n\r\r'); str = str.substring(0, 0) + '\n\n\r\r' + str.substring(0); pieceTable.insert(1, '\r\n\r\n'); @@ -1003,7 +1096,9 @@ suite('CRLF', () => { }); test('random bug 2', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\r\n\r'); str = str.substring(0, 0) + '\n\r\n\r' + str.substring(0); @@ -1018,7 +1113,9 @@ suite('CRLF', () => { }); test('random bug 3', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\n\n\r'); str = str.substring(0, 0) + '\n\n\n\r' + str.substring(0); @@ -1039,7 +1136,9 @@ suite('CRLF', () => { }); test('random bug 4', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\n\n\n'); str = str.substring(0, 0) + '\n\n\n\n' + str.substring(0); @@ -1057,7 +1156,9 @@ suite('CRLF', () => { }); test('random bug 5', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\n\n\n'); str = str.substring(0, 0) + '\n\n\n\n' + str.substring(0); @@ -1083,7 +1184,9 @@ suite('CRLF', () => { }); test('random bug 6', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\r\r\n'); str = str.substring(0, 0) + '\n\r\r\n' + str.substring(0); @@ -1107,7 +1210,9 @@ suite('CRLF', () => { }); test('random bug 8', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\r\n\n\r'); str = str.substring(0, 0) + '\r\n\n\r' + str.substring(0); @@ -1123,7 +1228,9 @@ suite('CRLF', () => { }); test('random bug 7', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\r\r\n\n'); str = str.substring(0, 0) + '\r\r\n\n' + str.substring(0); @@ -1139,7 +1246,9 @@ suite('CRLF', () => { test('random bug 10', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'qneW'); str = str.substring(0, 0) + 'qneW' + str.substring(0); @@ -1160,7 +1269,9 @@ suite('CRLF', () => { test('random bug 9', () => { let str = ''; - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, '\n\n\n\n'); str = str.substring(0, 0) + '\n\n\n\n' + str.substring(0); @@ -1181,14 +1292,20 @@ suite('CRLF', () => { }); suite('centralized lineStarts with CRLF', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('delete CR in CRLF 1', () => { - const pieceTable = createTextBuffer(['a\r\nb'], false); + const pieceTree = createTextBuffer(['a\r\nb'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(2, 2); assert.strictEqual(pieceTable.getLineCount(), 2); assertTreeInvariants(pieceTable); }); test('delete CR in CRLF 2', () => { - const pieceTable = createTextBuffer(['a\r\nb']); + const pieceTree = createTextBuffer(['a\r\nb']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(0, 2); assert.strictEqual(pieceTable.getLineCount(), 2); @@ -1197,7 +1314,10 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 1', () => { let str = '\n\n\r\r'; - const pieceTable = createTextBuffer(['\n\n\r\r'], false); + const pieceTree = createTextBuffer(['\n\n\r\r'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + pieceTable.insert(1, '\r\n\r\n'); str = str.substring(0, 1) + '\r\n\r\n' + str.substring(1); pieceTable.delete(5, 3); @@ -1211,7 +1331,9 @@ suite('centralized lineStarts with CRLF', () => { }); test('random bug 2', () => { let str = '\n\r\n\r'; - const pieceTable = createTextBuffer(['\n\r\n\r'], false); + const pieceTree = createTextBuffer(['\n\r\n\r'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(2, '\n\r\r\r'); str = str.substring(0, 2) + '\n\r\r\r' + str.substring(2); @@ -1225,7 +1347,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 3', () => { let str = '\n\n\n\r'; - const pieceTable = createTextBuffer(['\n\n\n\r'], false); + const pieceTree = createTextBuffer(['\n\n\n\r'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(2, 2); str = str.substring(0, 2) + str.substring(2 + 2); @@ -1245,7 +1369,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 4', () => { let str = '\n\n\n\n'; - const pieceTable = createTextBuffer(['\n\n\n\n'], false); + const pieceTree = createTextBuffer(['\n\n\n\n'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(3, 1); str = str.substring(0, 3) + str.substring(3 + 1); @@ -1262,7 +1388,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 5', () => { let str = '\n\n\n\n'; - const pieceTable = createTextBuffer(['\n\n\n\n'], false); + const pieceTree = createTextBuffer(['\n\n\n\n'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(3, 1); str = str.substring(0, 3) + str.substring(3 + 1); @@ -1287,7 +1415,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 6', () => { let str = '\n\r\r\n'; - const pieceTable = createTextBuffer(['\n\r\r\n'], false); + const pieceTree = createTextBuffer(['\n\r\r\n'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(4, '\r\n\n\r'); str = str.substring(0, 4) + '\r\n\n\r' + str.substring(4); @@ -1310,7 +1440,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 7', () => { let str = '\r\n\n\r'; - const pieceTable = createTextBuffer(['\r\n\n\r'], false); + const pieceTree = createTextBuffer(['\r\n\n\r'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.delete(1, 0); str = str.substring(0, 1) + str.substring(1 + 0); @@ -1325,7 +1457,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 8', () => { let str = '\r\r\n\n'; - const pieceTable = createTextBuffer(['\r\r\n\n'], false); + const pieceTree = createTextBuffer(['\r\r\n\n'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(4, '\r\n\n\r'); str = str.substring(0, 4) + '\r\n\n\r' + str.substring(4); @@ -1339,7 +1473,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 9', () => { let str = 'qneW'; - const pieceTable = createTextBuffer(['qneW'], false); + const pieceTree = createTextBuffer(['qneW'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(0, 'YhIl'); str = str.substring(0, 0) + 'YhIl' + str.substring(0); @@ -1358,7 +1494,9 @@ suite('centralized lineStarts with CRLF', () => { test('random bug 10', () => { let str = '\n\n\n\n'; - const pieceTable = createTextBuffer(['\n\n\n\n'], false); + const pieceTree = createTextBuffer(['\n\n\n\n'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); pieceTable.insert(3, '\n\r\n\r'); str = str.substring(0, 3) + '\n\r\n\r' + str.substring(3); @@ -1376,7 +1514,10 @@ suite('centralized lineStarts with CRLF', () => { }); test('random chunk bug 1', () => { - const pieceTable = createTextBuffer(['\n\r\r\n\n\n\r\n\r'], false); + const pieceTree = createTextBuffer(['\n\r\r\n\n\n\r\n\r'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + let str = '\n\r\r\n\n\n\r\n\r'; pieceTable.delete(0, 2); str = str.substring(0, 0) + str.substring(0 + 2); @@ -1391,9 +1532,11 @@ suite('centralized lineStarts with CRLF', () => { }); test('random chunk bug 2', () => { - const pieceTable = createTextBuffer([ + const pieceTree = createTextBuffer([ '\n\r\n\n\n\r\n\r\n\r\r\n\n\n\r\r\n\r\n' ], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = '\n\r\n\n\n\r\n\r\n\r\r\n\n\n\r\r\n\r\n'; pieceTable.insert(16, '\r\n\r\r'); str = str.substring(0, 16) + '\r\n\r\r' + str.substring(16); @@ -1412,7 +1555,9 @@ suite('centralized lineStarts with CRLF', () => { }); test('random chunk bug 3', () => { - const pieceTable = createTextBuffer(['\r\n\n\n\n\n\n\r\n'], false); + const pieceTree = createTextBuffer(['\r\n\n\n\n\n\n\r\n'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = '\r\n\n\n\n\n\n\r\n'; pieceTable.insert(4, '\n\n\r\n\r\r\n\n\r'); str = str.substring(0, 4) + '\n\n\r\n\r\r\n\n\r' + str.substring(4); @@ -1429,7 +1574,9 @@ suite('centralized lineStarts with CRLF', () => { }); test('random chunk bug 4', () => { - const pieceTable = createTextBuffer(['\n\r\n\r'], false); + const pieceTree = createTextBuffer(['\n\r\n\r'], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = '\n\r\n\r'; pieceTable.insert(4, '\n\n\r\n'); str = str.substring(0, 4) + '\n\n\r\n' + str.substring(4); @@ -1443,8 +1590,12 @@ suite('centralized lineStarts with CRLF', () => { }); suite('random is unsupervised', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('splitting large change buffer', function () { - const pieceTable = createTextBuffer([''], false); + const pieceTree = createTextBuffer([''], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = ''; pieceTable.insert(0, 'WUZ\nXVZY\n'); @@ -1478,8 +1629,9 @@ suite('random is unsupervised', () => { test('random insert delete', function () { this.timeout(500000); let str = ''; - const pieceTable = createTextBuffer([str], false); - + const pieceTree = createTextBuffer([str], false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); // let output = ''; for (let i = 0; i < 1000; i++) { if (Math.random() < 0.6) { @@ -1520,7 +1672,9 @@ suite('random is unsupervised', () => { chunks.push(randomStr(1000)); } - const pieceTable = createTextBuffer(chunks, false); + const pieceTree = createTextBuffer(chunks, false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = chunks.join(''); for (let i = 0; i < 1000; i++) { @@ -1553,7 +1707,9 @@ suite('random is unsupervised', () => { const chunks: string[] = []; chunks.push(randomStr(1000)); - const pieceTable = createTextBuffer(chunks, false); + const pieceTree = createTextBuffer(chunks, false); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = chunks.join(''); for (let i = 0; i < 50; i++) { @@ -1584,39 +1740,53 @@ suite('random is unsupervised', () => { }); suite('buffer api', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('equal', () => { const a = createTextBuffer(['abc']); const b = createTextBuffer(['ab', 'c']); const c = createTextBuffer(['abd']); const d = createTextBuffer(['abcd']); + ds.add(a); + ds.add(b); + ds.add(c); + ds.add(d); - assert(a.equal(b)); - assert(!a.equal(c)); - assert(!a.equal(d)); + assert(a.getPieceTree().equal(b.getPieceTree())); + assert(!a.getPieceTree().equal(c.getPieceTree())); + assert(!a.getPieceTree().equal(d.getPieceTree())); }); test('equal with more chunks', () => { const a = createTextBuffer(['ab', 'cd', 'e']); const b = createTextBuffer(['ab', 'c', 'de']); - assert(a.equal(b)); + ds.add(a); + ds.add(b); + assert(a.getPieceTree().equal(b.getPieceTree())); }); test('equal 2, empty buffer', () => { const a = createTextBuffer(['']); const b = createTextBuffer(['']); + ds.add(a); + ds.add(b); - assert(a.equal(b)); + assert(a.getPieceTree().equal(b.getPieceTree())); }); test('equal 3, empty buffer', () => { const a = createTextBuffer(['a']); const b = createTextBuffer(['']); + ds.add(a); + ds.add(b); - assert(!a.equal(b)); + assert(!a.getPieceTree().equal(b.getPieceTree())); }); test('getLineCharCode - issue #45735', () => { - const pieceTable = createTextBuffer(['LINE1\nline2']); + const pieceTree = createTextBuffer(['LINE1\nline2']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); assert.strictEqual(pieceTable.getLineCharCode(1, 0), 'L'.charCodeAt(0), 'L'); assert.strictEqual(pieceTable.getLineCharCode(1, 1), 'I'.charCodeAt(0), 'I'); assert.strictEqual(pieceTable.getLineCharCode(1, 2), 'N'.charCodeAt(0), 'N'); @@ -1632,7 +1802,9 @@ suite('buffer api', () => { test('getLineCharCode - issue #47733', () => { - const pieceTable = createTextBuffer(['', 'LINE1\n', 'line2']); + const pieceTree = createTextBuffer(['', 'LINE1\n', 'line2']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); assert.strictEqual(pieceTable.getLineCharCode(1, 0), 'L'.charCodeAt(0), 'L'); assert.strictEqual(pieceTable.getLineCharCode(1, 1), 'I'.charCodeAt(0), 'I'); assert.strictEqual(pieceTable.getLineCharCode(1, 2), 'N'.charCodeAt(0), 'N'); @@ -1648,8 +1820,12 @@ suite('buffer api', () => { }); suite('search offset cache', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('render white space exception', () => { - const pieceTable = createTextBuffer(['class Name{\n\t\n\t\t\tget() {\n\n\t\t\t}\n\t\t}']); + const pieceTree = createTextBuffer(['class Name{\n\t\n\t\t\tget() {\n\n\t\t\t}\n\t\t}']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = 'class Name{\n\t\n\t\t\tget() {\n\n\t\t\t}\n\t\t}'; pieceTable.insert(12, 's'); @@ -1705,7 +1881,9 @@ suite('search offset cache', () => { }); test('Line breaks replacement is not necessary when EOL is normalized', () => { - const pieceTable = createTextBuffer(['abc']); + const pieceTree = createTextBuffer(['abc']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = 'abc'; pieceTable.insert(3, 'def\nabc'); @@ -1717,7 +1895,9 @@ suite('search offset cache', () => { }); test('Line breaks replacement is not necessary when EOL is normalized 2', () => { - const pieceTable = createTextBuffer(['abc\n']); + const pieceTree = createTextBuffer(['abc\n']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = 'abc\n'; pieceTable.insert(4, 'def\nabc'); @@ -1729,7 +1909,9 @@ suite('search offset cache', () => { }); test('Line breaks replacement is not necessary when EOL is normalized 3', () => { - const pieceTable = createTextBuffer(['abc\n']); + const pieceTree = createTextBuffer(['abc\n']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = 'abc\n'; pieceTable.insert(2, 'def\nabc'); @@ -1741,7 +1923,9 @@ suite('search offset cache', () => { }); test('Line breaks replacement is not necessary when EOL is normalized 4', () => { - const pieceTable = createTextBuffer(['abc\n']); + const pieceTree = createTextBuffer(['abc\n']); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); let str = 'abc\n'; pieceTable.insert(3, 'def\nabc'); @@ -1766,6 +1950,8 @@ function getValueInSnapshot(snapshot: ITextSnapshot) { return ret; } suite('snapshot', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('bug #45564, piece tree pieces should be immutable', () => { const model = createTextModel('\n'); model.applyEdits([ @@ -1863,9 +2049,13 @@ suite('snapshot', () => { }); suite('chunk based search', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); + test('#45892. For some cases, the buffer is empty but we still try to search', () => { const pieceTree = createTextBuffer(['']); - pieceTree.delete(0, 1); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + pieceTable.delete(0, 1); const ret = pieceTree.findMatchesLineByLine(new Range(1, 1, 1, 1), new SearchData(/abc/, new WordCharacterClassifier(',./'), 'abc'), true, 1000); assert.strictEqual(ret.length, 0); }); @@ -1881,11 +2071,14 @@ suite('chunk based search', () => { '* [ ] task 3' ].join('\n') ]); - pieceTree.delete(0, 62); - pieceTree.delete(16, 1); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); - pieceTree.insert(16, ' '); - const ret = pieceTree.findMatchesLineByLine(new Range(1, 1, 4, 13), new SearchData(/\[/gi, new WordCharacterClassifier(',./'), '['), true, 1000); + pieceTable.delete(0, 62); + pieceTable.delete(16, 1); + + pieceTable.insert(16, ' '); + const ret = pieceTable.findMatchesLineByLine(new Range(1, 1, 4, 13), new SearchData(/\[/gi, new WordCharacterClassifier(',./'), '['), true, 1000); assert.strictEqual(ret.length, 3); assert.deepStrictEqual(ret[0].range, new Range(2, 3, 2, 4)); @@ -1900,13 +2093,16 @@ suite('chunk based search', () => { 'dbcabc' ].join('\n') ]); - pieceTree.delete(4, 1); - let ret = pieceTree.findMatchesLineByLine(new Range(2, 3, 2, 6), new SearchData(/a/gi, null, 'a'), true, 1000); + ds.add(pieceTree); + const pieceTable = pieceTree.getPieceTree(); + + pieceTable.delete(4, 1); + let ret = pieceTable.findMatchesLineByLine(new Range(2, 3, 2, 6), new SearchData(/a/gi, null, 'a'), true, 1000); assert.strictEqual(ret.length, 1); assert.deepStrictEqual(ret[0].range, new Range(2, 3, 2, 4)); - pieceTree.delete(4, 1); - ret = pieceTree.findMatchesLineByLine(new Range(2, 2, 2, 5), new SearchData(/a/gi, null, 'a'), true, 1000); + pieceTable.delete(4, 1); + ret = pieceTable.findMatchesLineByLine(new Range(2, 2, 2, 5), new SearchData(/a/gi, null, 'a'), true, 1000); assert.strictEqual(ret.length, 1); assert.deepStrictEqual(ret[0].range, new Range(2, 2, 2, 3)); }); diff --git a/src/vs/workbench/services/keybinding/test/browser/browserKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/browser/browserKeyboardMapper.test.ts index 3921c6f130e..0e2778b6191 100644 --- a/src/vs/workbench/services/keybinding/test/browser/browserKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/browserKeyboardMapper.test.ts @@ -16,6 +16,7 @@ import { TestNotificationService } from 'vs/platform/notification/test/common/te import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase { constructor(configurationService: IConfigurationService, notificationService: INotificationService, storageService: IStorageService, commandService: ICommandService) { @@ -35,17 +36,22 @@ class TestKeyboardMapperFactory extends BrowserKeyboardMapperFactoryBase { } suite('keyboard layout loader', () => { + const ds = ensureNoDisposablesAreLeakedInTestSuite(); let instantiationService: TestInstantiationService; let instance: TestKeyboardMapperFactory; setup(() => { instantiationService = new TestInstantiationService(); + const storageService = new TestStorageService(); const notitifcationService = instantiationService.stub(INotificationService, new TestNotificationService()); - const storageService = instantiationService.stub(IStorageService, new TestStorageService()); const configurationService = instantiationService.stub(IConfigurationService, new TestConfigurationService()); - const commandService = instantiationService.stub(ICommandService, {}); + + ds.add(instantiationService); + ds.add(storageService); + instance = new TestKeyboardMapperFactory(configurationService, notitifcationService, storageService, commandService); + ds.add(instance); }); teardown(() => { From 39cf14968a8616c5270c8e62817daa1a5277ffbe Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 7 Sep 2023 17:34:49 +0200 Subject: [PATCH 605/607] Fix some leaks found by ensureNoDisposables... (#192418) Part of #190503 --- src/vs/workbench/browser/parts/views/viewFilter.ts | 2 ++ src/vs/workbench/browser/parts/views/viewPane.ts | 4 ++-- .../comments/test/browser/commentsView.test.ts | 13 +++++++++---- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewFilter.ts b/src/vs/workbench/browser/parts/views/viewFilter.ts index 9986e46f084..cf47403d4cc 100644 --- a/src/vs/workbench/browser/parts/views/viewFilter.ts +++ b/src/vs/workbench/browser/parts/views/viewFilter.ts @@ -102,6 +102,8 @@ export class FilterWidget extends Widget { this.element = DOM.$('.viewpane-filter'); [this.filterInputBox, this.focusTracker] = this.createInput(this.element); + this._register(this.filterInputBox); + this._register(this.focusTracker); const controlsContainer = DOM.append(this.element, DOM.$('.viewpane-filter-controls')); this.filterBadge = this.createBadge(controlsContainer); diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 844c4bf5a96..11f2a5447c0 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -112,7 +112,7 @@ class ViewWelcomeController { @IContextKeyService private contextKeyService: IContextKeyService, ) { contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables); - Event.filter(viewsRegistry.onDidChangeViewWelcomeContent, id => id === this.id)(this.onDidChangeViewWelcomeContent, this, this.disposables); + this.disposables.add(Event.filter(viewsRegistry.onDidChangeViewWelcomeContent, id => id === this.id)(this.onDidChangeViewWelcomeContent, this, this.disposables)); this.onDidChangeViewWelcomeContent(); } @@ -240,7 +240,7 @@ export abstract class ViewPane extends Pane implements IView { this.menuActions = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])).createInstance(CompositeMenuActions, options.titleMenuId ?? MenuId.ViewTitle, MenuId.ViewTitleContext, { shouldForwardArgs: !options.donotForwardArgs })); this._register(this.menuActions.onDidChange(() => this.updateActions())); - this.viewWelcomeController = new ViewWelcomeController(this.id, contextKeyService); + this.viewWelcomeController = this._register(new ViewWelcomeController(this.id, contextKeyService)); } override get headerVisible(): boolean { diff --git a/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts b/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts index 83d2bf1ccea..21dff60d1b9 100644 --- a/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts +++ b/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts @@ -16,6 +16,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; class TestCommentThread implements CommentThread { isDocumentCommentThread(): this is CommentThread { @@ -70,6 +71,13 @@ export class TestViewDescriptorService implements Partial { + instantiationService.dispose(); + commentService.dispose(); + disposables.dispose(); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); let disposables: DisposableStore; let instantiationService: TestInstantiationService; @@ -85,10 +93,7 @@ suite('Comments View', function () { instantiationService.stub(ICommentService, commentService); }); - teardown(() => { - commentService.dispose(); - disposables.dispose(); - }); + test('collapse all', async function () { const view = instantiationService.createInstance(CommentsPanel, { id: 'comments', title: 'Comments' }); From 34eeab3023186c43331d54d464fd3a252ae3ff54 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:21:11 -0700 Subject: [PATCH 606/607] Fix Throttler dispose warning in unit tests This was happening because dispose was being overridden so the isDisposed check before a store is disposed again wasn't happening just for the throttler. Fixes #192427 Fixes #192434 Part of #192425 --- src/vs/base/parts/storage/common/storage.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index 304da70de7d..0351a7b42b3 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -123,7 +123,7 @@ export class Storage extends Disposable implements IStorage { private cache = new Map(); - private readonly flushDelayer = new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY); + private readonly flushDelayer = this._register(new ThrottledDelayer(Storage.DEFAULT_FLUSH_DELAY)); private pendingDeletes = new Set(); private pendingInserts = new Map(); @@ -406,12 +406,6 @@ export class Storage extends Disposable implements IStorage { isInMemory(): boolean { return this.options.hint === StorageHint.STORAGE_IN_MEMORY; } - - override dispose(): void { - this.flushDelayer.dispose(); - - super.dispose(); - } } export class InMemoryStorageDatabase implements IStorageDatabase { From 73036496967f356f77a6d9724a890e80854a4c8d Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 7 Sep 2023 10:23:47 -0700 Subject: [PATCH 607/607] Show theme id if it differs from label (#192459) The idea here is that when the label is localized, it will be different than the id... so the id will be displayed next to the label. This is similar to Configure Display Language... and also somewhat similar to the Command Palette showing the English label under the localized label. Fixes https://github.com/microsoft/vscode/issues/192223 --- .../contrib/themes/browser/themes.contribution.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index f245ba5a9bc..2dcb37944d7 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -328,6 +328,7 @@ class InstalledThemesPicker { quickpick.placeholder = this.placeholderMessage; quickpick.activeItems = [picks[autoFocusIndex] as ThemeItem]; quickpick.canSelectMany = false; + quickpick.matchOnDescription = true; quickpick.onDidAccept(async _ => { isCompleted = true; const theme = quickpick.selectedItems[0]; @@ -545,7 +546,13 @@ function isItem(i: QuickPickInput): i is ThemeItem { } function toEntry(theme: IWorkbenchTheme): ThemeItem { - const item: ThemeItem = { id: theme.id, theme: theme, label: theme.label, description: theme.description }; + const settingId = theme.settingsId ?? undefined; + const item: ThemeItem = { + id: theme.id, + theme: theme, + label: theme.label, + description: theme.description || (theme.label === settingId ? undefined : settingId), + }; if (theme.extensionData) { item.buttons = [configureButton]; }